Handle empty fields correctly
[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 if self.error then
1178 self.error[section] = "invalid"
1179 else
1180 self.error = { [section] = "invalid" }
1181 end
1182 self.map.save = false
1183 end
1184 if fvalue and not (fvalue == cvalue) then
1185 if self:write(section, fvalue) then
1186 -- Push events
1187 self.section.changed = true
1188 --luci.util.append(self.map.events, self.events)
1189 end
1190 end
1191 else -- Unset the UCI or error
1192 if self.rmempty or self.optional then
1193 if self:remove(section) then
1194 -- Push events
1195 self.section.changed = true
1196 --luci.util.append(self.map.events, self.events)
1197 end
1198 elseif cvalue ~= fvalue then
1199 self:write(section, fvalue)
1200 if self.error then
1201 self.error[section] = "missing"
1202 else
1203 self.error = { [section] = "missing" }
1204 end
1205 self.map.save = false
1206 end
1207 end
1208 end
1209
1210 -- Render if this value exists or if it is mandatory
1211 function AbstractValue.render(self, s, scope)
1212 if not self.optional or self:cfgvalue(s) or self:formcreated(s) then
1213 scope = scope or {}
1214 scope.section = s
1215 scope.cbid = self:cbid(s)
1216 scope.striptags = luci.util.striptags
1217
1218 scope.ifattr = function(cond,key,val)
1219 if cond then
1220 return string.format(
1221 ' %s="%s"', tostring(key),
1222 luci.util.pcdata(tostring( val
1223 or scope[key]
1224 or (type(self[key]) ~= "function" and self[key])
1225 or "" ))
1226 )
1227 else
1228 return ''
1229 end
1230 end
1231
1232 scope.attr = function(...)
1233 return scope.ifattr( true, ... )
1234 end
1235
1236 Node.render(self, scope)
1237 end
1238 end
1239
1240 -- Return the UCI value of this object
1241 function AbstractValue.cfgvalue(self, section)
1242 local value = self.map:get(section, self.option)
1243 if not value then
1244 return nil
1245 elseif not self.cast or self.cast == type(value) then
1246 return value
1247 elseif self.cast == "string" then
1248 if type(value) == "table" then
1249 return value[1]
1250 end
1251 elseif self.cast == "table" then
1252 return luci.util.split(value, "%s+", nil, true)
1253 end
1254 end
1255
1256 -- Validate the form value
1257 function AbstractValue.validate(self, value)
1258 return value
1259 end
1260
1261 AbstractValue.transform = AbstractValue.validate
1262
1263
1264 -- Write to UCI
1265 function AbstractValue.write(self, section, value)
1266 return self.map:set(section, self.option, value)
1267 end
1268
1269 -- Remove from UCI
1270 function AbstractValue.remove(self, section)
1271 return self.map:del(section, self.option)
1272 end
1273
1274
1275
1276
1277 --[[
1278 Value - A one-line value
1279 maxlength: The maximum length
1280 ]]--
1281 Value = class(AbstractValue)
1282
1283 function Value.__init__(self, ...)
1284 AbstractValue.__init__(self, ...)
1285 self.template = "cbi/value"
1286 self.keylist = {}
1287 self.vallist = {}
1288 end
1289
1290 function Value.value(self, key, val)
1291 val = val or key
1292 table.insert(self.keylist, tostring(key))
1293 table.insert(self.vallist, tostring(val))
1294 end
1295
1296
1297 -- DummyValue - This does nothing except being there
1298 DummyValue = class(AbstractValue)
1299
1300 function DummyValue.__init__(self, ...)
1301 AbstractValue.__init__(self, ...)
1302 self.template = "cbi/dvalue"
1303 self.value = nil
1304 end
1305
1306 function DummyValue.cfgvalue(self, section)
1307 local value
1308 if self.value then
1309 if type(self.value) == "function" then
1310 value = self:value(section)
1311 else
1312 value = self.value
1313 end
1314 else
1315 value = AbstractValue.cfgvalue(self, section)
1316 end
1317 return value
1318 end
1319
1320 function DummyValue.parse(self)
1321
1322 end
1323
1324
1325 --[[
1326 Flag - A flag being enabled or disabled
1327 ]]--
1328 Flag = class(AbstractValue)
1329
1330 function Flag.__init__(self, ...)
1331 AbstractValue.__init__(self, ...)
1332 self.template = "cbi/fvalue"
1333
1334 self.enabled = "1"
1335 self.disabled = "0"
1336 end
1337
1338 -- A flag can only have two states: set or unset
1339 function Flag.parse(self, section)
1340 local fvalue = self:formvalue(section)
1341
1342 if fvalue then
1343 fvalue = self.enabled
1344 else
1345 fvalue = self.disabled
1346 end
1347
1348 if fvalue == self.enabled or (not self.optional and not self.rmempty) then
1349 if not(fvalue == self:cfgvalue(section)) then
1350 self:write(section, fvalue)
1351 end
1352 else
1353 self:remove(section)
1354 end
1355 end
1356
1357
1358
1359 --[[
1360 ListValue - A one-line value predefined in a list
1361 widget: The widget that will be used (select, radio)
1362 ]]--
1363 ListValue = class(AbstractValue)
1364
1365 function ListValue.__init__(self, ...)
1366 AbstractValue.__init__(self, ...)
1367 self.template = "cbi/lvalue"
1368
1369 self.keylist = {}
1370 self.vallist = {}
1371 self.size = 1
1372 self.widget = "select"
1373 end
1374
1375 function ListValue.prepare(self, ...)
1376 AbstractValue.prepare(self, ...)
1377 if not self.override_scheme
1378 and self.map:get_scheme(self.section.sectiontype, self.option) then
1379 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1380 if self.value and vs.valuelist and not self.override_values then
1381 for k, v in ipairs(vs.valuelist) do
1382 local deps = {}
1383 if not self.override_dependencies
1384 and vs.enum_depends and vs.enum_depends[v.value] then
1385 for i, dep in ipairs(vs.enum_depends[v.value]) do
1386 table.insert(deps, _uvl_strip_remote_dependencies(dep))
1387 end
1388 end
1389 self:value(v.value, v.title or v.value, unpack(deps))
1390 end
1391 end
1392 end
1393 end
1394
1395 function ListValue.value(self, key, val, ...)
1396 if luci.util.contains(self.keylist, key) then
1397 return
1398 end
1399
1400 val = val or key
1401 table.insert(self.keylist, tostring(key))
1402 table.insert(self.vallist, tostring(val))
1403
1404 for i, deps in ipairs({...}) do
1405 table.insert(self.deps, {add = "-"..key, deps=deps})
1406 end
1407 end
1408
1409 function ListValue.validate(self, val)
1410 if luci.util.contains(self.keylist, val) then
1411 return val
1412 else
1413 return nil
1414 end
1415 end
1416
1417
1418
1419 --[[
1420 MultiValue - Multiple delimited values
1421 widget: The widget that will be used (select, checkbox)
1422 delimiter: The delimiter that will separate the values (default: " ")
1423 ]]--
1424 MultiValue = class(AbstractValue)
1425
1426 function MultiValue.__init__(self, ...)
1427 AbstractValue.__init__(self, ...)
1428 self.template = "cbi/mvalue"
1429
1430 self.keylist = {}
1431 self.vallist = {}
1432
1433 self.widget = "checkbox"
1434 self.delimiter = " "
1435 end
1436
1437 function MultiValue.render(self, ...)
1438 if self.widget == "select" and not self.size then
1439 self.size = #self.vallist
1440 end
1441
1442 AbstractValue.render(self, ...)
1443 end
1444
1445 function MultiValue.value(self, key, val)
1446 if luci.util.contains(self.keylist, key) then
1447 return
1448 end
1449
1450 val = val or key
1451 table.insert(self.keylist, tostring(key))
1452 table.insert(self.vallist, tostring(val))
1453 end
1454
1455 function MultiValue.valuelist(self, section)
1456 local val = self:cfgvalue(section)
1457
1458 if not(type(val) == "string") then
1459 return {}
1460 end
1461
1462 return luci.util.split(val, self.delimiter)
1463 end
1464
1465 function MultiValue.validate(self, val)
1466 val = (type(val) == "table") and val or {val}
1467
1468 local result
1469
1470 for i, value in ipairs(val) do
1471 if luci.util.contains(self.keylist, value) then
1472 result = result and (result .. self.delimiter .. value) or value
1473 end
1474 end
1475
1476 return result
1477 end
1478
1479
1480 StaticList = class(MultiValue)
1481
1482 function StaticList.__init__(self, ...)
1483 MultiValue.__init__(self, ...)
1484 self.cast = "table"
1485 self.valuelist = self.cfgvalue
1486
1487 if not self.override_scheme
1488 and self.map:get_scheme(self.section.sectiontype, self.option) then
1489 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1490 if self.value and vs.values and not self.override_values then
1491 for k, v in pairs(vs.values) do
1492 self:value(k, v)
1493 end
1494 end
1495 end
1496 end
1497
1498 function StaticList.validate(self, value)
1499 value = (type(value) == "table") and value or {value}
1500
1501 local valid = {}
1502 for i, v in ipairs(value) do
1503 if luci.util.contains(self.vallist, v) then
1504 table.insert(valid, v)
1505 end
1506 end
1507 return valid
1508 end
1509
1510
1511 DynamicList = class(AbstractValue)
1512
1513 function DynamicList.__init__(self, ...)
1514 AbstractValue.__init__(self, ...)
1515 self.template = "cbi/dynlist"
1516 self.cast = "table"
1517 self.keylist = {}
1518 self.vallist = {}
1519 end
1520
1521 function DynamicList.value(self, key, val)
1522 val = val or key
1523 table.insert(self.keylist, tostring(key))
1524 table.insert(self.vallist, tostring(val))
1525 end
1526
1527 function DynamicList.formvalue(self, section)
1528 local value = AbstractValue.formvalue(self, section)
1529 value = (type(value) == "table") and value or {value}
1530
1531 local valid = {}
1532 for i, v in ipairs(value) do
1533 if v and #v > 0
1534 and not self.map:formvalue("cbi.rle."..section.."."..self.option.."."..i)
1535 and not self.map:formvalue("cbi.rle."..section.."."..self.option.."."..i..".x") then
1536 table.insert(valid, v)
1537 end
1538 end
1539
1540 return valid
1541 end
1542
1543
1544 --[[
1545 TextValue - A multi-line value
1546 rows: Rows
1547 ]]--
1548 TextValue = class(AbstractValue)
1549
1550 function TextValue.__init__(self, ...)
1551 AbstractValue.__init__(self, ...)
1552 self.template = "cbi/tvalue"
1553 end
1554
1555 --[[
1556 Button
1557 ]]--
1558 Button = class(AbstractValue)
1559
1560 function Button.__init__(self, ...)
1561 AbstractValue.__init__(self, ...)
1562 self.template = "cbi/button"
1563 self.inputstyle = nil
1564 self.rmempty = true
1565 end
1566
1567
1568 FileUpload = class(AbstractValue)
1569
1570 function FileUpload.__init__(self, ...)
1571 AbstractValue.__init__(self, ...)
1572 self.template = "cbi/upload"
1573 if not self.map.upload_fields then
1574 self.map.upload_fields = { self }
1575 else
1576 self.map.upload_fields[#self.map.upload_fields+1] = self
1577 end
1578 end
1579
1580 function FileUpload.formcreated(self, section)
1581 return AbstractValue.formcreated(self, section) or
1582 self.map:formvalue("cbi.rlf."..section.."."..self.option) or
1583 self.map:formvalue("cbi.rlf."..section.."."..self.option..".x")
1584 end
1585
1586 function FileUpload.cfgvalue(self, section)
1587 local val = AbstractValue.cfgvalue(self, section)
1588 if val and luci.fs.access(val) then
1589 return val
1590 end
1591 return nil
1592 end
1593
1594 function FileUpload.formvalue(self, section)
1595 local val = AbstractValue.formvalue(self, section)
1596 if val then
1597 if not self.map:formvalue("cbi.rlf."..section.."."..self.option) and
1598 not self.map:formvalue("cbi.rlf."..section.."."..self.option..".x")
1599 then
1600 return val
1601 end
1602 luci.fs.unlink(val)
1603 self.value = nil
1604 end
1605 return nil
1606 end
1607
1608 function FileUpload.remove(self, section)
1609 local val = AbstractValue.formvalue(self, section)
1610 if val and luci.fs.access(val) then luci.fs.unlink(val) end
1611 return AbstractValue.remove(self, section)
1612 end
1613
1614
1615 FileBrowser = class(AbstractValue)
1616
1617 function FileBrowser.__init__(self, ...)
1618 AbstractValue.__init__(self, ...)
1619 self.template = "cbi/browser"
1620 end