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