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