Merge pull request #626 from dangowrt/improve-wpa-eap
[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 for i, config in ipairs(self.parsechain) do
392 self.uci:save(config)
393 end
394 self:_run_hooks("on_after_save")
395 if (not self.proceed and self.flow.autoapply) or luci.http.formvalue("cbi.apply") then
396 self:_run_hooks("on_before_commit")
397 for i, config in ipairs(self.parsechain) do
398 self.uci:commit(config)
399
400 -- Refresh data because commit changes section names
401 self.uci:load(config)
402 end
403 self:_run_hooks("on_commit", "on_after_commit", "on_before_apply")
404 if self.apply_on_parse then
405 self.uci:apply(self.parsechain)
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 end
1204
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
1220 if created then
1221 AbstractSection.parse_optionals(self, created)
1222 end
1223 end
1224
1225 if self.sortable then
1226 local stval = RESORT_PREFIX .. self.config .. "." .. self.sectiontype
1227 local order = self.map:formvalue(stval)
1228 if order and #order > 0 then
1229 local sid
1230 local num = 0
1231 for sid in util.imatch(order) do
1232 self.map.uci:reorder(self.config, sid, num)
1233 num = num + 1
1234 end
1235 self.changed = (num > 0)
1236 end
1237 end
1238
1239 if created or self.changed then
1240 self:push_events()
1241 end
1242 end
1243
1244 -- Verifies scope of sections
1245 function TypedSection.checkscope(self, section)
1246 -- Check if we are not excluded
1247 if self.filter and not self:filter(section) then
1248 return nil
1249 end
1250
1251 -- Check if at least one dependency is met
1252 if #self.deps > 0 and self:cfgvalue(section) then
1253 local stat = false
1254
1255 for k, v in ipairs(self.deps) do
1256 if self:cfgvalue(section)[v.option] == v.value then
1257 stat = true
1258 end
1259 end
1260
1261 if not stat then
1262 return nil
1263 end
1264 end
1265
1266 return self:validate(section)
1267 end
1268
1269
1270 -- Dummy validate function
1271 function TypedSection.validate(self, section)
1272 return section
1273 end
1274
1275
1276 --[[
1277 AbstractValue - An abstract Value Type
1278 null: Value can be empty
1279 valid: A function returning the value if it is valid otherwise nil
1280 depends: A table of option => value pairs of which one must be true
1281 default: The default value
1282 size: The size of the input fields
1283 rmempty: Unset value if empty
1284 optional: This value is optional (see AbstractSection.optionals)
1285 ]]--
1286 AbstractValue = class(Node)
1287
1288 function AbstractValue.__init__(self, map, section, option, ...)
1289 Node.__init__(self, ...)
1290 self.section = section
1291 self.option = option
1292 self.map = map
1293 self.config = map.config
1294 self.tag_invalid = {}
1295 self.tag_missing = {}
1296 self.tag_reqerror = {}
1297 self.tag_error = {}
1298 self.deps = {}
1299 --self.cast = "string"
1300
1301 self.track_missing = false
1302 self.rmempty = true
1303 self.default = nil
1304 self.size = nil
1305 self.optional = false
1306 end
1307
1308 function AbstractValue.prepare(self)
1309 self.cast = self.cast or "string"
1310 end
1311
1312 -- Add a dependencie to another section field
1313 function AbstractValue.depends(self, field, value)
1314 local deps
1315 if type(field) == "string" then
1316 deps = {}
1317 deps[field] = value
1318 else
1319 deps = field
1320 end
1321
1322 table.insert(self.deps, deps)
1323 end
1324
1325 -- Serialize dependencies
1326 function AbstractValue.deplist2json(self, section, deplist)
1327 local deps, i, d = { }
1328
1329 if type(self.deps) == "table" then
1330 for i, d in ipairs(deplist or self.deps) do
1331 local a, k, v = { }
1332 for k, v in pairs(d) do
1333 if k:find("!", 1, true) then
1334 a[k] = v
1335 elseif k:find(".", 1, true) then
1336 a['cbid.%s' % k] = v
1337 else
1338 a['cbid.%s.%s.%s' %{ self.config, section, k }] = v
1339 end
1340 end
1341 deps[#deps+1] = a
1342 end
1343 end
1344
1345 return util.serialize_json(deps)
1346 end
1347
1348 -- Generates the unique CBID
1349 function AbstractValue.cbid(self, section)
1350 return "cbid."..self.map.config.."."..section.."."..self.option
1351 end
1352
1353 -- Return whether this object should be created
1354 function AbstractValue.formcreated(self, section)
1355 local key = "cbi.opt."..self.config.."."..section
1356 return (self.map:formvalue(key) == self.option)
1357 end
1358
1359 -- Returns the formvalue for this object
1360 function AbstractValue.formvalue(self, section)
1361 return self.map:formvalue(self:cbid(section))
1362 end
1363
1364 function AbstractValue.additional(self, value)
1365 self.optional = value
1366 end
1367
1368 function AbstractValue.mandatory(self, value)
1369 self.rmempty = not value
1370 end
1371
1372 function AbstractValue.add_error(self, section, type, msg)
1373 self.error = self.error or { }
1374 self.error[section] = msg or type
1375
1376 self.section.error = self.section.error or { }
1377 self.section.error[section] = self.section.error[section] or { }
1378 table.insert(self.section.error[section], msg or type)
1379
1380 if type == "invalid" then
1381 self.tag_invalid[section] = true
1382 elseif type == "missing" then
1383 self.tag_missing[section] = true
1384 end
1385
1386 self.tag_error[section] = true
1387 self.map.save = false
1388 end
1389
1390 function AbstractValue.parse(self, section, novld)
1391 local fvalue = self:formvalue(section)
1392 local cvalue = self:cfgvalue(section)
1393
1394 -- If favlue and cvalue are both tables and have the same content
1395 -- make them identical
1396 if type(fvalue) == "table" and type(cvalue) == "table" then
1397 local equal = #fvalue == #cvalue
1398 if equal then
1399 for i=1, #fvalue do
1400 if cvalue[i] ~= fvalue[i] then
1401 equal = false
1402 end
1403 end
1404 end
1405 if equal then
1406 fvalue = cvalue
1407 end
1408 end
1409
1410 if fvalue and #fvalue > 0 then -- If we have a form value, write it to UCI
1411 local val_err
1412 fvalue, val_err = self:validate(fvalue, section)
1413 fvalue = self:transform(fvalue)
1414
1415 if not fvalue and not novld then
1416 self:add_error(section, "invalid", val_err)
1417 end
1418
1419 if fvalue and (self.forcewrite or not (fvalue == cvalue)) then
1420 if self:write(section, fvalue) then
1421 -- Push events
1422 self.section.changed = true
1423 --luci.util.append(self.map.events, self.events)
1424 end
1425 end
1426 else -- Unset the UCI or error
1427 if self.rmempty or self.optional then
1428 if self:remove(section) then
1429 -- Push events
1430 self.section.changed = true
1431 --luci.util.append(self.map.events, self.events)
1432 end
1433 elseif cvalue ~= fvalue and not novld then
1434 -- trigger validator with nil value to get custom user error msg.
1435 local _, val_err = self:validate(nil, section)
1436 self:add_error(section, "missing", val_err)
1437 end
1438 end
1439 end
1440
1441 -- Render if this value exists or if it is mandatory
1442 function AbstractValue.render(self, s, scope)
1443 if not self.optional or self.section:has_tabs() or self:cfgvalue(s) or self:formcreated(s) then
1444 scope = scope or {}
1445 scope.section = s
1446 scope.cbid = self:cbid(s)
1447 Node.render(self, scope)
1448 end
1449 end
1450
1451 -- Return the UCI value of this object
1452 function AbstractValue.cfgvalue(self, section)
1453 local value
1454 if self.tag_error[section] then
1455 value = self:formvalue(section)
1456 else
1457 value = self.map:get(section, self.option)
1458 end
1459
1460 if not value then
1461 return nil
1462 elseif not self.cast or self.cast == type(value) then
1463 return value
1464 elseif self.cast == "string" then
1465 if type(value) == "table" then
1466 return value[1]
1467 end
1468 elseif self.cast == "table" then
1469 return { value }
1470 end
1471 end
1472
1473 -- Validate the form value
1474 function AbstractValue.validate(self, value)
1475 if self.datatype and value then
1476 if type(value) == "table" then
1477 local v
1478 for _, v in ipairs(value) do
1479 if v and #v > 0 and not verify_datatype(self.datatype, v) then
1480 return nil
1481 end
1482 end
1483 else
1484 if not verify_datatype(self.datatype, value) then
1485 return nil
1486 end
1487 end
1488 end
1489
1490 return value
1491 end
1492
1493 AbstractValue.transform = AbstractValue.validate
1494
1495
1496 -- Write to UCI
1497 function AbstractValue.write(self, section, value)
1498 return self.map:set(section, self.option, value)
1499 end
1500
1501 -- Remove from UCI
1502 function AbstractValue.remove(self, section)
1503 return self.map:del(section, self.option)
1504 end
1505
1506
1507
1508
1509 --[[
1510 Value - A one-line value
1511 maxlength: The maximum length
1512 ]]--
1513 Value = class(AbstractValue)
1514
1515 function Value.__init__(self, ...)
1516 AbstractValue.__init__(self, ...)
1517 self.template = "cbi/value"
1518 self.keylist = {}
1519 self.vallist = {}
1520 self.readonly = nil
1521 end
1522
1523 function Value.reset_values(self)
1524 self.keylist = {}
1525 self.vallist = {}
1526 end
1527
1528 function Value.value(self, key, val)
1529 val = val or key
1530 table.insert(self.keylist, tostring(key))
1531 table.insert(self.vallist, tostring(val))
1532 end
1533
1534 function Value.parse(self, section, novld)
1535 if self.readonly then return end
1536 AbstractValue.parse(self, section, novld)
1537 end
1538
1539 -- DummyValue - This does nothing except being there
1540 DummyValue = class(AbstractValue)
1541
1542 function DummyValue.__init__(self, ...)
1543 AbstractValue.__init__(self, ...)
1544 self.template = "cbi/dvalue"
1545 self.value = nil
1546 end
1547
1548 function DummyValue.cfgvalue(self, section)
1549 local value
1550 if self.value then
1551 if type(self.value) == "function" then
1552 value = self:value(section)
1553 else
1554 value = self.value
1555 end
1556 else
1557 value = AbstractValue.cfgvalue(self, section)
1558 end
1559 return value
1560 end
1561
1562 function DummyValue.parse(self)
1563
1564 end
1565
1566
1567 --[[
1568 Flag - A flag being enabled or disabled
1569 ]]--
1570 Flag = class(AbstractValue)
1571
1572 function Flag.__init__(self, ...)
1573 AbstractValue.__init__(self, ...)
1574 self.template = "cbi/fvalue"
1575
1576 self.enabled = "1"
1577 self.disabled = "0"
1578 self.default = self.disabled
1579 end
1580
1581 -- A flag can only have two states: set or unset
1582 function Flag.parse(self, section, novld)
1583 local fexists = self.map:formvalue(
1584 FEXIST_PREFIX .. self.config .. "." .. section .. "." .. self.option)
1585
1586 if fexists then
1587 local fvalue = self:formvalue(section) and self.enabled or self.disabled
1588 local cvalue = self:cfgvalue(section)
1589 local val_err
1590 fvalue, val_err = self:validate(fvalue, section)
1591 if not fvalue then
1592 if not novld then
1593 self:add_error(section, "invalid", val_err)
1594 end
1595 return
1596 end
1597 if fvalue == self.default and (self.optional or self.rmempty) then
1598 self:remove(section)
1599 else
1600 self:write(section, fvalue)
1601 end
1602 if (fvalue ~= cvalue) then self.section.changed = true end
1603 else
1604 self:remove(section)
1605 self.section.changed = true
1606 end
1607 end
1608
1609 function Flag.cfgvalue(self, section)
1610 return AbstractValue.cfgvalue(self, section) or self.default
1611 end
1612 function Flag.validate(self, value)
1613 return value
1614 end
1615
1616 --[[
1617 ListValue - A one-line value predefined in a list
1618 widget: The widget that will be used (select, radio)
1619 ]]--
1620 ListValue = class(AbstractValue)
1621
1622 function ListValue.__init__(self, ...)
1623 AbstractValue.__init__(self, ...)
1624 self.template = "cbi/lvalue"
1625
1626 self.keylist = {}
1627 self.vallist = {}
1628 self.deplist = {}
1629 self.size = 1
1630 self.widget = "select"
1631 end
1632
1633 function ListValue.reset_values(self)
1634 self.keylist = {}
1635 self.vallist = {}
1636 end
1637
1638 function ListValue.value(self, key, val, ...)
1639 if luci.util.contains(self.keylist, key) then
1640 return
1641 end
1642
1643 val = val or key
1644 table.insert(self.keylist, tostring(key))
1645 table.insert(self.vallist, tostring(val))
1646 table.insert(self.deplist, {...})
1647 end
1648
1649 function ListValue.validate(self, val)
1650 if luci.util.contains(self.keylist, val) then
1651 return val
1652 else
1653 return nil
1654 end
1655 end
1656
1657
1658
1659 --[[
1660 MultiValue - Multiple delimited values
1661 widget: The widget that will be used (select, checkbox)
1662 delimiter: The delimiter that will separate the values (default: " ")
1663 ]]--
1664 MultiValue = class(AbstractValue)
1665
1666 function MultiValue.__init__(self, ...)
1667 AbstractValue.__init__(self, ...)
1668 self.template = "cbi/mvalue"
1669
1670 self.keylist = {}
1671 self.vallist = {}
1672
1673 self.widget = "checkbox"
1674 self.delimiter = " "
1675 end
1676
1677 function MultiValue.render(self, ...)
1678 if self.widget == "select" and not self.size then
1679 self.size = #self.vallist
1680 end
1681
1682 AbstractValue.render(self, ...)
1683 end
1684
1685 function MultiValue.reset_values(self)
1686 self.keylist = {}
1687 self.vallist = {}
1688 end
1689
1690 function MultiValue.value(self, key, val)
1691 if luci.util.contains(self.keylist, key) then
1692 return
1693 end
1694
1695 val = val or key
1696 table.insert(self.keylist, tostring(key))
1697 table.insert(self.vallist, tostring(val))
1698 end
1699
1700 function MultiValue.valuelist(self, section)
1701 local val = self:cfgvalue(section)
1702
1703 if not(type(val) == "string") then
1704 return {}
1705 end
1706
1707 return luci.util.split(val, self.delimiter)
1708 end
1709
1710 function MultiValue.validate(self, val)
1711 val = (type(val) == "table") and val or {val}
1712
1713 local result
1714
1715 for i, value in ipairs(val) do
1716 if luci.util.contains(self.keylist, value) then
1717 result = result and (result .. self.delimiter .. value) or value
1718 end
1719 end
1720
1721 return result
1722 end
1723
1724
1725 StaticList = class(MultiValue)
1726
1727 function StaticList.__init__(self, ...)
1728 MultiValue.__init__(self, ...)
1729 self.cast = "table"
1730 self.valuelist = self.cfgvalue
1731
1732 if not self.override_scheme
1733 and self.map:get_scheme(self.section.sectiontype, self.option) then
1734 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1735 if self.value and vs.values and not self.override_values then
1736 for k, v in pairs(vs.values) do
1737 self:value(k, v)
1738 end
1739 end
1740 end
1741 end
1742
1743 function StaticList.validate(self, value)
1744 value = (type(value) == "table") and value or {value}
1745
1746 local valid = {}
1747 for i, v in ipairs(value) do
1748 if luci.util.contains(self.keylist, v) then
1749 table.insert(valid, v)
1750 end
1751 end
1752 return valid
1753 end
1754
1755
1756 DynamicList = class(AbstractValue)
1757
1758 function DynamicList.__init__(self, ...)
1759 AbstractValue.__init__(self, ...)
1760 self.template = "cbi/dynlist"
1761 self.cast = "table"
1762 self.keylist = {}
1763 self.vallist = {}
1764 end
1765
1766 function DynamicList.reset_values(self)
1767 self.keylist = {}
1768 self.vallist = {}
1769 end
1770
1771 function DynamicList.value(self, key, val)
1772 val = val or key
1773 table.insert(self.keylist, tostring(key))
1774 table.insert(self.vallist, tostring(val))
1775 end
1776
1777 function DynamicList.write(self, section, value)
1778 local t = { }
1779
1780 if type(value) == "table" then
1781 local x
1782 for _, x in ipairs(value) do
1783 if x and #x > 0 then
1784 t[#t+1] = x
1785 end
1786 end
1787 else
1788 t = { value }
1789 end
1790
1791 if self.cast == "string" then
1792 value = table.concat(t, " ")
1793 else
1794 value = t
1795 end
1796
1797 return AbstractValue.write(self, section, value)
1798 end
1799
1800 function DynamicList.cfgvalue(self, section)
1801 local value = AbstractValue.cfgvalue(self, section)
1802
1803 if type(value) == "string" then
1804 local x
1805 local t = { }
1806 for x in value:gmatch("%S+") do
1807 if #x > 0 then
1808 t[#t+1] = x
1809 end
1810 end
1811 value = t
1812 end
1813
1814 return value
1815 end
1816
1817 function DynamicList.formvalue(self, section)
1818 local value = AbstractValue.formvalue(self, section)
1819
1820 if type(value) == "string" then
1821 if self.cast == "string" then
1822 local x
1823 local t = { }
1824 for x in value:gmatch("%S+") do
1825 t[#t+1] = x
1826 end
1827 value = t
1828 else
1829 value = { value }
1830 end
1831 end
1832
1833 return value
1834 end
1835
1836
1837 --[[
1838 TextValue - A multi-line value
1839 rows: Rows
1840 ]]--
1841 TextValue = class(AbstractValue)
1842
1843 function TextValue.__init__(self, ...)
1844 AbstractValue.__init__(self, ...)
1845 self.template = "cbi/tvalue"
1846 end
1847
1848 --[[
1849 Button
1850 ]]--
1851 Button = class(AbstractValue)
1852
1853 function Button.__init__(self, ...)
1854 AbstractValue.__init__(self, ...)
1855 self.template = "cbi/button"
1856 self.inputstyle = nil
1857 self.rmempty = true
1858 self.unsafeupload = false
1859 end
1860
1861
1862 FileUpload = class(AbstractValue)
1863
1864 function FileUpload.__init__(self, ...)
1865 AbstractValue.__init__(self, ...)
1866 self.template = "cbi/upload"
1867 if not self.map.upload_fields then
1868 self.map.upload_fields = { self }
1869 else
1870 self.map.upload_fields[#self.map.upload_fields+1] = self
1871 end
1872 end
1873
1874 function FileUpload.formcreated(self, section)
1875 if self.unsafeupload then
1876 return AbstractValue.formcreated(self, section) or
1877 self.map:formvalue("cbi.rlf."..section.."."..self.option) or
1878 self.map:formvalue("cbi.rlf."..section.."."..self.option..".x") or
1879 self.map:formvalue("cbid."..self.map.config.."."..section.."."..self.option..".textbox")
1880 else
1881 return AbstractValue.formcreated(self, section) or
1882 self.map:formvalue("cbid."..self.map.config.."."..section.."."..self.option..".textbox")
1883 end
1884 end
1885
1886 function FileUpload.cfgvalue(self, section)
1887 local val = AbstractValue.cfgvalue(self, section)
1888 if val and fs.access(val) then
1889 return val
1890 end
1891 return nil
1892 end
1893
1894 -- If we have a new value, use it
1895 -- otherwise use old value
1896 -- deletion should be managed by a separate button object
1897 -- unless self.unsafeupload is set in which case if the user
1898 -- choose to remove the old file we do so.
1899 -- Also, allow to specify (via textbox) a file already on router
1900 function FileUpload.formvalue(self, section)
1901 local val = AbstractValue.formvalue(self, section)
1902 if val then
1903 if self.unsafeupload then
1904 if not self.map:formvalue("cbi.rlf."..section.."."..self.option) and
1905 not self.map:formvalue("cbi.rlf."..section.."."..self.option..".x")
1906 then
1907 return val
1908 end
1909 fs.unlink(val)
1910 self.value = nil
1911 return nil
1912 elseif val ~= "" then
1913 return val
1914 end
1915 end
1916 val = luci.http.formvalue("cbid."..self.map.config.."."..section.."."..self.option..".textbox")
1917 if val == "" then
1918 val = nil
1919 end
1920 if not self.unsafeupload then
1921 if not val then
1922 val = self.map:formvalue("cbi.rlf."..section.."."..self.option)
1923 end
1924 end
1925 return val
1926 end
1927
1928 function FileUpload.remove(self, section)
1929 if self.unsafeupload then
1930 local val = AbstractValue.formvalue(self, section)
1931 if val and fs.access(val) then fs.unlink(val) end
1932 return AbstractValue.remove(self, section)
1933 else
1934 return nil
1935 end
1936 end
1937
1938 FileBrowser = class(AbstractValue)
1939
1940 function FileBrowser.__init__(self, ...)
1941 AbstractValue.__init__(self, ...)
1942 self.template = "cbi/browser"
1943 end