Scope compatibility fixes
[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 return self.map:del(section)
773 end
774
775 -- Creates the section
776 function AbstractSection.create(self, section)
777 local stat
778
779 if section then
780 stat = section:match("^%w+$") and self.map:set(section, nil, self.sectiontype)
781 else
782 section = self.map:add(self.sectiontype)
783 stat = section
784 end
785
786 if stat then
787 for k,v in pairs(self.children) do
788 if v.default then
789 self.map:set(section, v.option, v.default)
790 end
791 end
792
793 for k,v in pairs(self.defaults) do
794 self.map:set(section, k, v)
795 end
796 end
797
798 return stat
799 end
800
801
802 SimpleSection = class(AbstractSection)
803
804 function SimpleSection.__init__(self, form, ...)
805 AbstractSection.__init__(self, form, nil, ...)
806 self.template = "cbi/nullsection"
807 end
808
809
810 Table = class(AbstractSection)
811
812 function Table.__init__(self, form, data, ...)
813 local datasource = {}
814 datasource.config = "table"
815 self.data = data
816
817 datasource.formvalue = Map.formvalue
818 datasource.formvaluetable = Map.formvaluetable
819 datasource.readinput = true
820
821 function datasource.get(self, section, option)
822 return data[section] and data[section][option]
823 end
824
825 function datasource.submitstate(self)
826 return Map.formvalue(self, "cbi.submit")
827 end
828
829 function datasource.del(...)
830 return true
831 end
832
833 function datasource.get_scheme()
834 return nil
835 end
836
837 AbstractSection.__init__(self, datasource, "table", ...)
838 self.template = "cbi/tblsection"
839 self.rowcolors = true
840 self.anonymous = true
841 end
842
843 function Table.parse(self, readinput)
844 self.map.readinput = (readinput ~= false)
845 for i, k in ipairs(self:cfgsections()) do
846 if self.map:submitstate() then
847 Node.parse(self, k)
848 end
849 end
850 end
851
852 function Table.cfgsections(self)
853 local sections = {}
854
855 for i, v in luci.util.kspairs(self.data) do
856 table.insert(sections, i)
857 end
858
859 return sections
860 end
861
862
863
864 --[[
865 NamedSection - A fixed configuration section defined by its name
866 ]]--
867 NamedSection = class(AbstractSection)
868
869 function NamedSection.__init__(self, map, section, stype, ...)
870 AbstractSection.__init__(self, map, stype, ...)
871 Node._i18n(self, map.config, section, nil, ...)
872
873 -- Defaults
874 self.addremove = false
875
876 -- Use defaults from UVL
877 if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
878 local vs = self.map:get_scheme(self.sectiontype)
879 self.addremove = not vs.unique and not vs.required
880 self.dynamic = vs.dynamic
881 self.title = self.title or vs.title
882 self.description = self.description or vs.descr
883 end
884
885 self.template = "cbi/nsection"
886 self.section = section
887 end
888
889 function NamedSection.parse(self, novld)
890 local s = self.section
891 local active = self:cfgvalue(s)
892
893 if self.addremove then
894 local path = self.config.."."..s
895 if active then -- Remove the section
896 if self.map:formvalue("cbi.rns."..path) and self:remove(s) then
897 self:push_events()
898 return
899 end
900 else -- Create and apply default values
901 if self.map:formvalue("cbi.cns."..path) then
902 self:create(s)
903 return
904 end
905 end
906 end
907
908 if active then
909 AbstractSection.parse_dynamic(self, s)
910 if self.map:submitstate() then
911 Node.parse(self, s)
912
913 if not novld and not self.override_scheme and self.map.scheme then
914 _uvl_validate_section(self, s)
915 end
916 end
917 AbstractSection.parse_optionals(self, s)
918
919 if self.changed then
920 self:push_events()
921 end
922 end
923 end
924
925
926 --[[
927 TypedSection - A (set of) configuration section(s) defined by the type
928 addremove: Defines whether the user can add/remove sections of this type
929 anonymous: Allow creating anonymous sections
930 validate: a validation function returning nil if the section is invalid
931 ]]--
932 TypedSection = class(AbstractSection)
933
934 function TypedSection.__init__(self, map, type, ...)
935 AbstractSection.__init__(self, map, type, ...)
936 Node._i18n(self, map.config, type, nil, ...)
937
938 self.template = "cbi/tsection"
939 self.deps = {}
940 self.anonymous = false
941
942 -- Use defaults from UVL
943 if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
944 local vs = self.map:get_scheme(self.sectiontype)
945 self.addremove = not vs.unique and not vs.required
946 self.dynamic = vs.dynamic
947 self.anonymous = not vs.named
948 self.title = self.title or vs.title
949 self.description = self.description or vs.descr
950 end
951 end
952
953 -- Return all matching UCI sections for this TypedSection
954 function TypedSection.cfgsections(self)
955 local sections = {}
956 self.map.uci:foreach(self.map.config, self.sectiontype,
957 function (section)
958 if self:checkscope(section[".name"]) then
959 table.insert(sections, section[".name"])
960 end
961 end)
962
963 return sections
964 end
965
966 -- Limits scope to sections that have certain option => value pairs
967 function TypedSection.depends(self, option, value)
968 table.insert(self.deps, {option=option, value=value})
969 end
970
971 function TypedSection.parse(self, novld)
972 if self.addremove then
973 -- Remove
974 local crval = REMOVE_PREFIX .. self.config
975 local name = self.map:formvaluetable(crval)
976 for k,v in pairs(name) do
977 if k:sub(-2) == ".x" then
978 k = k:sub(1, #k - 2)
979 end
980 if self:cfgvalue(k) and self:checkscope(k) then
981 self:remove(k)
982 end
983 end
984 end
985
986 local co
987 for i, k in ipairs(self:cfgsections()) do
988 AbstractSection.parse_dynamic(self, k)
989 if self.map:submitstate() then
990 Node.parse(self, k, novld)
991
992 if not novld and not self.override_scheme and self.map.scheme then
993 _uvl_validate_section(self, k)
994 end
995 end
996 AbstractSection.parse_optionals(self, k)
997 end
998
999 if self.addremove then
1000 -- Create
1001 local created
1002 local crval = CREATE_PREFIX .. self.config .. "." .. self.sectiontype
1003 local name = self.map:formvalue(crval)
1004 if self.anonymous then
1005 if name then
1006 created = self:create()
1007 end
1008 else
1009 if name then
1010 -- Ignore if it already exists
1011 if self:cfgvalue(name) then
1012 name = nil;
1013 end
1014
1015 name = self:checkscope(name)
1016
1017 if not name then
1018 self.err_invalid = true
1019 end
1020
1021 if name and #name > 0 then
1022 created = self:create(name) and name
1023 if not created then
1024 self.invalid_cts = true
1025 end
1026 end
1027 end
1028 end
1029
1030 if created then
1031 AbstractSection.parse_optionals(self, created)
1032 end
1033 end
1034
1035 if created or self.changed then
1036 self:push_events()
1037 end
1038 end
1039
1040 -- Verifies scope of sections
1041 function TypedSection.checkscope(self, section)
1042 -- Check if we are not excluded
1043 if self.filter and not self:filter(section) then
1044 return nil
1045 end
1046
1047 -- Check if at least one dependency is met
1048 if #self.deps > 0 and self:cfgvalue(section) then
1049 local stat = false
1050
1051 for k, v in ipairs(self.deps) do
1052 if self:cfgvalue(section)[v.option] == v.value then
1053 stat = true
1054 end
1055 end
1056
1057 if not stat then
1058 return nil
1059 end
1060 end
1061
1062 return self:validate(section)
1063 end
1064
1065
1066 -- Dummy validate function
1067 function TypedSection.validate(self, section)
1068 return section
1069 end
1070
1071
1072 --[[
1073 AbstractValue - An abstract Value Type
1074 null: Value can be empty
1075 valid: A function returning the value if it is valid otherwise nil
1076 depends: A table of option => value pairs of which one must be true
1077 default: The default value
1078 size: The size of the input fields
1079 rmempty: Unset value if empty
1080 optional: This value is optional (see AbstractSection.optionals)
1081 ]]--
1082 AbstractValue = class(Node)
1083
1084 function AbstractValue.__init__(self, map, section, option, ...)
1085 Node.__init__(self, ...)
1086 self.section = section
1087 self.option = option
1088 self.map = map
1089 self.config = map.config
1090 self.tag_invalid = {}
1091 self.tag_missing = {}
1092 self.tag_reqerror = {}
1093 self.tag_error = {}
1094 self.deps = {}
1095 --self.cast = "string"
1096
1097 self.track_missing = false
1098 self.rmempty = true
1099 self.default = nil
1100 self.size = nil
1101 self.optional = false
1102 end
1103
1104 function AbstractValue.prepare(self)
1105 -- Use defaults from UVL
1106 if not self.override_scheme
1107 and self.map:get_scheme(self.section.sectiontype, self.option) then
1108 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1109 if self.cast == nil then
1110 self.cast = (vs.type == "list") and "list" or "string"
1111 end
1112 self.title = self.title or vs.title
1113 self.description = self.description or vs.descr
1114 if self.default == nil then
1115 self.default = vs.default
1116 end
1117
1118 if vs.depends and not self.override_dependencies then
1119 for i, deps in ipairs(vs.depends) do
1120 deps = _uvl_strip_remote_dependencies(deps)
1121 if next(deps) then
1122 self:depends(deps)
1123 end
1124 end
1125 end
1126 end
1127
1128 self.cast = self.cast or "string"
1129 end
1130
1131 -- Add a dependencie to another section field
1132 function AbstractValue.depends(self, field, value)
1133 local deps
1134 if type(field) == "string" then
1135 deps = {}
1136 deps[field] = value
1137 else
1138 deps = field
1139 end
1140
1141 table.insert(self.deps, {deps=deps, add=""})
1142 end
1143
1144 -- Generates the unique CBID
1145 function AbstractValue.cbid(self, section)
1146 return "cbid."..self.map.config.."."..section.."."..self.option
1147 end
1148
1149 -- Return whether this object should be created
1150 function AbstractValue.formcreated(self, section)
1151 local key = "cbi.opt."..self.config.."."..section
1152 return (self.map:formvalue(key) == self.option)
1153 end
1154
1155 -- Returns the formvalue for this object
1156 function AbstractValue.formvalue(self, section)
1157 return self.map:formvalue(self:cbid(section))
1158 end
1159
1160 function AbstractValue.additional(self, value)
1161 self.optional = value
1162 end
1163
1164 function AbstractValue.mandatory(self, value)
1165 self.rmempty = not value
1166 end
1167
1168 function AbstractValue.parse(self, section, novld)
1169 local fvalue = self:formvalue(section)
1170 local cvalue = self:cfgvalue(section)
1171
1172 if fvalue and #fvalue > 0 then -- If we have a form value, write it to UCI
1173 fvalue = self:transform(self:validate(fvalue, section))
1174 if not fvalue and not novld then
1175 if self.error then
1176 self.error[section] = "invalid"
1177 else
1178 self.error = { [section] = "invalid" }
1179 end
1180 self.map.save = false
1181 end
1182 if fvalue and not (fvalue == cvalue) then
1183 if self:write(section, fvalue) then
1184 -- Push events
1185 self.section.changed = true
1186 --luci.util.append(self.map.events, self.events)
1187 end
1188 end
1189 else -- Unset the UCI or error
1190 if self.rmempty or self.optional then
1191 if self:remove(section) then
1192 -- Push events
1193 self.section.changed = true
1194 --luci.util.append(self.map.events, self.events)
1195 end
1196 elseif cvalue ~= fvalue and not novld then
1197 self:write(section, fvalue or "")
1198 if self.error then
1199 self.error[section] = "missing"
1200 else
1201 self.error = { [section] = "missing" }
1202 end
1203 self.map.save = false
1204 end
1205 end
1206 end
1207
1208 -- Render if this value exists or if it is mandatory
1209 function AbstractValue.render(self, s, scope)
1210 if not self.optional or self:cfgvalue(s) or self:formcreated(s) then
1211 scope = scope or {}
1212 scope.section = s
1213 scope.cbid = self:cbid(s)
1214 scope.striptags = luci.util.striptags
1215
1216 scope.ifattr = function(cond,key,val)
1217 if cond then
1218 return string.format(
1219 ' %s="%s"', tostring(key),
1220 luci.util.pcdata(tostring( val
1221 or scope[key]
1222 or (type(self[key]) ~= "function" and self[key])
1223 or "" ))
1224 )
1225 else
1226 return ''
1227 end
1228 end
1229
1230 scope.attr = function(...)
1231 return scope.ifattr( true, ... )
1232 end
1233
1234 Node.render(self, scope)
1235 end
1236 end
1237
1238 -- Return the UCI value of this object
1239 function AbstractValue.cfgvalue(self, section)
1240 local value = self.map:get(section, self.option)
1241 if not value then
1242 return nil
1243 elseif not self.cast or self.cast == type(value) then
1244 return value
1245 elseif self.cast == "string" then
1246 if type(value) == "table" then
1247 return value[1]
1248 end
1249 elseif self.cast == "table" then
1250 return luci.util.split(value, "%s+", nil, true)
1251 end
1252 end
1253
1254 -- Validate the form value
1255 function AbstractValue.validate(self, value)
1256 return value
1257 end
1258
1259 AbstractValue.transform = AbstractValue.validate
1260
1261
1262 -- Write to UCI
1263 function AbstractValue.write(self, section, value)
1264 return self.map:set(section, self.option, value)
1265 end
1266
1267 -- Remove from UCI
1268 function AbstractValue.remove(self, section)
1269 return self.map:del(section, self.option)
1270 end
1271
1272
1273
1274
1275 --[[
1276 Value - A one-line value
1277 maxlength: The maximum length
1278 ]]--
1279 Value = class(AbstractValue)
1280
1281 function Value.__init__(self, ...)
1282 AbstractValue.__init__(self, ...)
1283 self.template = "cbi/value"
1284 self.keylist = {}
1285 self.vallist = {}
1286 end
1287
1288 function Value.value(self, key, val)
1289 val = val or key
1290 table.insert(self.keylist, tostring(key))
1291 table.insert(self.vallist, tostring(val))
1292 end
1293
1294
1295 -- DummyValue - This does nothing except being there
1296 DummyValue = class(AbstractValue)
1297
1298 function DummyValue.__init__(self, ...)
1299 AbstractValue.__init__(self, ...)
1300 self.template = "cbi/dvalue"
1301 self.value = nil
1302 end
1303
1304 function DummyValue.cfgvalue(self, section)
1305 local value
1306 if self.value then
1307 if type(self.value) == "function" then
1308 value = self:value(section)
1309 else
1310 value = self.value
1311 end
1312 else
1313 value = AbstractValue.cfgvalue(self, section)
1314 end
1315 return value
1316 end
1317
1318 function DummyValue.parse(self)
1319
1320 end
1321
1322
1323 --[[
1324 Flag - A flag being enabled or disabled
1325 ]]--
1326 Flag = class(AbstractValue)
1327
1328 function Flag.__init__(self, ...)
1329 AbstractValue.__init__(self, ...)
1330 self.template = "cbi/fvalue"
1331
1332 self.enabled = "1"
1333 self.disabled = "0"
1334 end
1335
1336 -- A flag can only have two states: set or unset
1337 function Flag.parse(self, section)
1338 local fvalue = self:formvalue(section)
1339
1340 if fvalue then
1341 fvalue = self.enabled
1342 else
1343 fvalue = self.disabled
1344 end
1345
1346 if fvalue == self.enabled or (not self.optional and not self.rmempty) then
1347 if not(fvalue == self:cfgvalue(section)) then
1348 self:write(section, fvalue)
1349 end
1350 else
1351 self:remove(section)
1352 end
1353 end
1354
1355
1356
1357 --[[
1358 ListValue - A one-line value predefined in a list
1359 widget: The widget that will be used (select, radio)
1360 ]]--
1361 ListValue = class(AbstractValue)
1362
1363 function ListValue.__init__(self, ...)
1364 AbstractValue.__init__(self, ...)
1365 self.template = "cbi/lvalue"
1366
1367 self.keylist = {}
1368 self.vallist = {}
1369 self.size = 1
1370 self.widget = "select"
1371 end
1372
1373 function ListValue.prepare(self, ...)
1374 AbstractValue.prepare(self, ...)
1375 if not self.override_scheme
1376 and self.map:get_scheme(self.section.sectiontype, self.option) then
1377 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1378 if self.value and vs.valuelist and not self.override_values then
1379 for k, v in ipairs(vs.valuelist) do
1380 local deps = {}
1381 if not self.override_dependencies
1382 and vs.enum_depends and vs.enum_depends[v.value] then
1383 for i, dep in ipairs(vs.enum_depends[v.value]) do
1384 table.insert(deps, _uvl_strip_remote_dependencies(dep))
1385 end
1386 end
1387 self:value(v.value, v.title or v.value, unpack(deps))
1388 end
1389 end
1390 end
1391 end
1392
1393 function ListValue.value(self, key, val, ...)
1394 if luci.util.contains(self.keylist, key) then
1395 return
1396 end
1397
1398 val = val or key
1399 table.insert(self.keylist, tostring(key))
1400 table.insert(self.vallist, tostring(val))
1401
1402 for i, deps in ipairs({...}) do
1403 table.insert(self.deps, {add = "-"..key, deps=deps})
1404 end
1405 end
1406
1407 function ListValue.validate(self, val)
1408 if luci.util.contains(self.keylist, val) then
1409 return val
1410 else
1411 return nil
1412 end
1413 end
1414
1415
1416
1417 --[[
1418 MultiValue - Multiple delimited values
1419 widget: The widget that will be used (select, checkbox)
1420 delimiter: The delimiter that will separate the values (default: " ")
1421 ]]--
1422 MultiValue = class(AbstractValue)
1423
1424 function MultiValue.__init__(self, ...)
1425 AbstractValue.__init__(self, ...)
1426 self.template = "cbi/mvalue"
1427
1428 self.keylist = {}
1429 self.vallist = {}
1430
1431 self.widget = "checkbox"
1432 self.delimiter = " "
1433 end
1434
1435 function MultiValue.render(self, ...)
1436 if self.widget == "select" and not self.size then
1437 self.size = #self.vallist
1438 end
1439
1440 AbstractValue.render(self, ...)
1441 end
1442
1443 function MultiValue.value(self, key, val)
1444 if luci.util.contains(self.keylist, key) then
1445 return
1446 end
1447
1448 val = val or key
1449 table.insert(self.keylist, tostring(key))
1450 table.insert(self.vallist, tostring(val))
1451 end
1452
1453 function MultiValue.valuelist(self, section)
1454 local val = self:cfgvalue(section)
1455
1456 if not(type(val) == "string") then
1457 return {}
1458 end
1459
1460 return luci.util.split(val, self.delimiter)
1461 end
1462
1463 function MultiValue.validate(self, val)
1464 val = (type(val) == "table") and val or {val}
1465
1466 local result
1467
1468 for i, value in ipairs(val) do
1469 if luci.util.contains(self.keylist, value) then
1470 result = result and (result .. self.delimiter .. value) or value
1471 end
1472 end
1473
1474 return result
1475 end
1476
1477
1478 StaticList = class(MultiValue)
1479
1480 function StaticList.__init__(self, ...)
1481 MultiValue.__init__(self, ...)
1482 self.cast = "table"
1483 self.valuelist = self.cfgvalue
1484
1485 if not self.override_scheme
1486 and self.map:get_scheme(self.section.sectiontype, self.option) then
1487 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1488 if self.value and vs.values and not self.override_values then
1489 for k, v in pairs(vs.values) do
1490 self:value(k, v)
1491 end
1492 end
1493 end
1494 end
1495
1496 function StaticList.validate(self, value)
1497 value = (type(value) == "table") and value or {value}
1498
1499 local valid = {}
1500 for i, v in ipairs(value) do
1501 if luci.util.contains(self.vallist, v) then
1502 table.insert(valid, v)
1503 end
1504 end
1505 return valid
1506 end
1507
1508
1509 DynamicList = class(AbstractValue)
1510
1511 function DynamicList.__init__(self, ...)
1512 AbstractValue.__init__(self, ...)
1513 self.template = "cbi/dynlist"
1514 self.cast = "table"
1515 self.keylist = {}
1516 self.vallist = {}
1517 end
1518
1519 function DynamicList.value(self, key, val)
1520 val = val or key
1521 table.insert(self.keylist, tostring(key))
1522 table.insert(self.vallist, tostring(val))
1523 end
1524
1525 function DynamicList.formvalue(self, section)
1526 local value = AbstractValue.formvalue(self, section)
1527 value = (type(value) == "table") and value or {value}
1528
1529 local valid = {}
1530 for i, v in ipairs(value) do
1531 if v and #v > 0
1532 and not self.map:formvalue("cbi.rle."..section.."."..self.option.."."..i)
1533 and not self.map:formvalue("cbi.rle."..section.."."..self.option.."."..i..".x") then
1534 table.insert(valid, v)
1535 end
1536 end
1537
1538 return valid
1539 end
1540
1541
1542 --[[
1543 TextValue - A multi-line value
1544 rows: Rows
1545 ]]--
1546 TextValue = class(AbstractValue)
1547
1548 function TextValue.__init__(self, ...)
1549 AbstractValue.__init__(self, ...)
1550 self.template = "cbi/tvalue"
1551 end
1552
1553 --[[
1554 Button
1555 ]]--
1556 Button = class(AbstractValue)
1557
1558 function Button.__init__(self, ...)
1559 AbstractValue.__init__(self, ...)
1560 self.template = "cbi/button"
1561 self.inputstyle = nil
1562 self.rmempty = true
1563 end
1564
1565
1566 FileUpload = class(AbstractValue)
1567
1568 function FileUpload.__init__(self, ...)
1569 AbstractValue.__init__(self, ...)
1570 self.template = "cbi/upload"
1571 if not self.map.upload_fields then
1572 self.map.upload_fields = { self }
1573 else
1574 self.map.upload_fields[#self.map.upload_fields+1] = self
1575 end
1576 end
1577
1578 function FileUpload.formcreated(self, section)
1579 return AbstractValue.formcreated(self, section) or
1580 self.map:formvalue("cbi.rlf."..section.."."..self.option) or
1581 self.map:formvalue("cbi.rlf."..section.."."..self.option..".x")
1582 end
1583
1584 function FileUpload.cfgvalue(self, section)
1585 local val = AbstractValue.cfgvalue(self, section)
1586 if val and luci.fs.access(val) then
1587 return val
1588 end
1589 return nil
1590 end
1591
1592 function FileUpload.formvalue(self, section)
1593 local val = AbstractValue.formvalue(self, section)
1594 if val then
1595 if not self.map:formvalue("cbi.rlf."..section.."."..self.option) and
1596 not self.map:formvalue("cbi.rlf."..section.."."..self.option..".x")
1597 then
1598 return val
1599 end
1600 luci.fs.unlink(val)
1601 self.value = nil
1602 end
1603 return nil
1604 end
1605
1606 function FileUpload.remove(self, section)
1607 local val = AbstractValue.formvalue(self, section)
1608 if val and luci.fs.access(val) then luci.fs.unlink(val) end
1609 return AbstractValue.remove(self, section)
1610 end
1611
1612
1613 FileBrowser = class(AbstractValue)
1614
1615 function FileBrowser.__init__(self, ...)
1616 AbstractValue.__init__(self, ...)
1617 self.template = "cbi/browser"
1618 end