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