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