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