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