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