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