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