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