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