Propagate CBI status via HTTP-Header
[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 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.del(...)
695 return true
696 end
697
698 function datasource.get_scheme()
699 return nil
700 end
701
702 AbstractSection.__init__(self, datasource, "table", ...)
703 self.template = "cbi/tblsection"
704 self.rowcolors = true
705 self.anonymous = true
706 end
707
708 function Table.parse(self)
709 for i, k in ipairs(self:cfgsections()) do
710 if self.map:submitstate() then
711 Node.parse(self, k)
712 end
713 end
714 end
715
716 function Table.cfgsections(self)
717 local sections = {}
718
719 for i, v in luci.util.kspairs(self.data) do
720 table.insert(sections, i)
721 end
722
723 return sections
724 end
725
726
727
728 --[[
729 NamedSection - A fixed configuration section defined by its name
730 ]]--
731 NamedSection = class(AbstractSection)
732
733 function NamedSection.__init__(self, map, section, stype, ...)
734 AbstractSection.__init__(self, map, stype, ...)
735 Node._i18n(self, map.config, section, nil, ...)
736
737 -- Defaults
738 self.addremove = false
739
740 -- Use defaults from UVL
741 if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
742 local vs = self.map:get_scheme(self.sectiontype)
743 self.addremove = not vs.unique and not vs.required
744 self.dynamic = vs.dynamic
745 self.title = self.title or vs.title
746 self.description = self.description or vs.descr
747 end
748
749 self.template = "cbi/nsection"
750 self.section = section
751 end
752
753 function NamedSection.parse(self, novld)
754 local s = self.section
755 local active = self:cfgvalue(s)
756
757 if self.addremove then
758 local path = self.config.."."..s
759 if active then -- Remove the section
760 if luci.http.formvalue("cbi.rns."..path) and self:remove(s) then
761 return
762 end
763 else -- Create and apply default values
764 if luci.http.formvalue("cbi.cns."..path) then
765 self:create(s)
766 return
767 end
768 end
769 end
770
771 if active then
772 AbstractSection.parse_dynamic(self, s)
773 if self.map:submitstate() then
774 Node.parse(self, s)
775
776 if not novld and not self.override_scheme and self.map.scheme then
777 _uvl_validate_section(self, s)
778 end
779 end
780 AbstractSection.parse_optionals(self, s)
781 end
782 end
783
784
785 --[[
786 TypedSection - A (set of) configuration section(s) defined by the type
787 addremove: Defines whether the user can add/remove sections of this type
788 anonymous: Allow creating anonymous sections
789 validate: a validation function returning nil if the section is invalid
790 ]]--
791 TypedSection = class(AbstractSection)
792
793 function TypedSection.__init__(self, map, type, ...)
794 AbstractSection.__init__(self, map, type, ...)
795 Node._i18n(self, map.config, type, nil, ...)
796
797 self.template = "cbi/tsection"
798 self.deps = {}
799 self.anonymous = false
800
801 -- Use defaults from UVL
802 if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
803 local vs = self.map:get_scheme(self.sectiontype)
804 self.addremove = not vs.unique and not vs.required
805 self.dynamic = vs.dynamic
806 self.anonymous = not vs.named
807 self.title = self.title or vs.title
808 self.description = self.description or vs.descr
809 end
810 end
811
812 -- Return all matching UCI sections for this TypedSection
813 function TypedSection.cfgsections(self)
814 local sections = {}
815 self.map.uci:foreach(self.map.config, self.sectiontype,
816 function (section)
817 if self:checkscope(section[".name"]) then
818 table.insert(sections, section[".name"])
819 end
820 end)
821
822 return sections
823 end
824
825 -- Limits scope to sections that have certain option => value pairs
826 function TypedSection.depends(self, option, value)
827 table.insert(self.deps, {option=option, value=value})
828 end
829
830 function TypedSection.parse(self, novld)
831 if self.addremove then
832 -- Remove
833 local crval = REMOVE_PREFIX .. self.config
834 local name = luci.http.formvaluetable(crval)
835 for k,v in pairs(name) do
836 if k:sub(-2) == ".x" then
837 k = k:sub(1, #k - 2)
838 end
839 if self:cfgvalue(k) and self:checkscope(k) then
840 self:remove(k)
841 end
842 end
843 end
844
845 local co
846 for i, k in ipairs(self:cfgsections()) do
847 AbstractSection.parse_dynamic(self, k)
848 if self.map:submitstate() then
849 Node.parse(self, k)
850
851 if not novld and not self.override_scheme and self.map.scheme then
852 _uvl_validate_section(self, k)
853 end
854 end
855 AbstractSection.parse_optionals(self, k)
856 end
857
858 if self.addremove then
859 -- Create
860 local created
861 local crval = CREATE_PREFIX .. self.config .. "." .. self.sectiontype
862 local name = luci.http.formvalue(crval)
863 if self.anonymous then
864 if name then
865 created = self:create()
866 end
867 else
868 if name then
869 -- Ignore if it already exists
870 if self:cfgvalue(name) then
871 name = nil;
872 end
873
874 name = self:checkscope(name)
875
876 if not name then
877 self.err_invalid = true
878 end
879
880 if name and #name > 0 then
881 created = self:create(name) and name
882 if not created then
883 self.invalid_cts = true
884 end
885 end
886 end
887 end
888
889 if created then
890 AbstractSection.parse_optionals(self, created)
891 end
892 end
893 end
894
895 -- Verifies scope of sections
896 function TypedSection.checkscope(self, section)
897 -- Check if we are not excluded
898 if self.filter and not self:filter(section) then
899 return nil
900 end
901
902 -- Check if at least one dependency is met
903 if #self.deps > 0 and self:cfgvalue(section) then
904 local stat = false
905
906 for k, v in ipairs(self.deps) do
907 if self:cfgvalue(section)[v.option] == v.value then
908 stat = true
909 end
910 end
911
912 if not stat then
913 return nil
914 end
915 end
916
917 return self:validate(section)
918 end
919
920
921 -- Dummy validate function
922 function TypedSection.validate(self, section)
923 return section
924 end
925
926
927 --[[
928 AbstractValue - An abstract Value Type
929 null: Value can be empty
930 valid: A function returning the value if it is valid otherwise nil
931 depends: A table of option => value pairs of which one must be true
932 default: The default value
933 size: The size of the input fields
934 rmempty: Unset value if empty
935 optional: This value is optional (see AbstractSection.optionals)
936 ]]--
937 AbstractValue = class(Node)
938
939 function AbstractValue.__init__(self, map, section, option, ...)
940 Node.__init__(self, ...)
941 self.section = section
942 self.option = option
943 self.map = map
944 self.config = map.config
945 self.tag_invalid = {}
946 self.tag_missing = {}
947 self.tag_reqerror = {}
948 self.tag_error = {}
949 self.deps = {}
950 --self.cast = "string"
951
952 self.track_missing = false
953 --self.rmempty = false
954 self.default = nil
955 self.size = nil
956 self.optional = false
957 end
958
959 function AbstractValue.prepare(self)
960 -- Use defaults from UVL
961 if not self.override_scheme
962 and self.map:get_scheme(self.section.sectiontype, self.option) then
963 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
964 if self.rmempty == nil then
965 self.rmempty = not vs.required
966 end
967 if self.cast == nil then
968 self.cast = (vs.type == "list") and "list" or "string"
969 end
970 self.title = self.title or vs.title
971 self.description = self.description or vs.descr
972 if self.default == nil then
973 self.default = vs.default
974 end
975
976 if vs.depends and not self.override_dependencies then
977 for i, deps in ipairs(vs.depends) do
978 deps = _uvl_strip_remote_dependencies(deps)
979 if next(deps) then
980 self:depends(deps)
981 end
982 end
983 end
984 end
985
986 self.cast = self.cast or "string"
987 end
988
989 -- Add a dependencie to another section field
990 function AbstractValue.depends(self, field, value)
991 local deps
992 if type(field) == "string" then
993 deps = {}
994 deps[field] = value
995 else
996 deps = field
997 end
998
999 table.insert(self.deps, {deps=deps, add=""})
1000 end
1001
1002 -- Generates the unique CBID
1003 function AbstractValue.cbid(self, section)
1004 return "cbid."..self.map.config.."."..section.."."..self.option
1005 end
1006
1007 -- Return whether this object should be created
1008 function AbstractValue.formcreated(self, section)
1009 local key = "cbi.opt."..self.config.."."..section
1010 return (luci.http.formvalue(key) == self.option)
1011 end
1012
1013 -- Returns the formvalue for this object
1014 function AbstractValue.formvalue(self, section)
1015 return luci.http.formvalue(self:cbid(section))
1016 end
1017
1018 function AbstractValue.additional(self, value)
1019 self.optional = value
1020 end
1021
1022 function AbstractValue.mandatory(self, value)
1023 self.rmempty = not value
1024 end
1025
1026 function AbstractValue.parse(self, section)
1027 local fvalue = self:formvalue(section)
1028 local cvalue = self:cfgvalue(section)
1029
1030 if fvalue and #fvalue > 0 then -- If we have a form value, write it to UCI
1031 fvalue = self:transform(self:validate(fvalue, section))
1032 if not fvalue then
1033 self.tag_invalid[section] = true
1034 end
1035 if fvalue and not (fvalue == cvalue) then
1036 self:write(section, fvalue)
1037 end
1038 else -- Unset the UCI or error
1039 if self.rmempty or self.optional then
1040 self:remove(section)
1041 elseif self.track_missing and (not fvalue or fvalue ~= cvalue) then
1042 self.tag_missing[section] = true
1043 end
1044 end
1045 end
1046
1047 -- Render if this value exists or if it is mandatory
1048 function AbstractValue.render(self, s, scope)
1049 if not self.optional or self:cfgvalue(s) or self:formcreated(s) then
1050 scope = scope or {}
1051 scope.section = s
1052 scope.cbid = self:cbid(s)
1053 scope.striptags = luci.util.striptags
1054
1055 scope.ifattr = function(cond,key,val)
1056 if cond then
1057 return string.format(
1058 ' %s="%s"', tostring(key),
1059 luci.util.pcdata(tostring( val
1060 or scope[key]
1061 or (type(self[key]) ~= "function" and self[key])
1062 or "" ))
1063 )
1064 else
1065 return ''
1066 end
1067 end
1068
1069 scope.attr = function(...)
1070 return scope.ifattr( true, ... )
1071 end
1072
1073 Node.render(self, scope)
1074 end
1075 end
1076
1077 -- Return the UCI value of this object
1078 function AbstractValue.cfgvalue(self, section)
1079 local value = self.map:get(section, self.option)
1080 if not value then
1081 return nil
1082 elseif not self.cast or self.cast == type(value) then
1083 return value
1084 elseif self.cast == "string" then
1085 if type(value) == "table" then
1086 return value[1]
1087 end
1088 elseif self.cast == "table" then
1089 return luci.util.split(value, "%s+", nil, true)
1090 end
1091 end
1092
1093 -- Validate the form value
1094 function AbstractValue.validate(self, value)
1095 return value
1096 end
1097
1098 AbstractValue.transform = AbstractValue.validate
1099
1100
1101 -- Write to UCI
1102 function AbstractValue.write(self, section, value)
1103 return self.map:set(section, self.option, value)
1104 end
1105
1106 -- Remove from UCI
1107 function AbstractValue.remove(self, section)
1108 return self.map:del(section, self.option)
1109 end
1110
1111
1112
1113
1114 --[[
1115 Value - A one-line value
1116 maxlength: The maximum length
1117 ]]--
1118 Value = class(AbstractValue)
1119
1120 function Value.__init__(self, ...)
1121 AbstractValue.__init__(self, ...)
1122 self.template = "cbi/value"
1123 self.keylist = {}
1124 self.vallist = {}
1125 end
1126
1127 function Value.value(self, key, val)
1128 val = val or key
1129 table.insert(self.keylist, tostring(key))
1130 table.insert(self.vallist, tostring(val))
1131 end
1132
1133
1134 -- DummyValue - This does nothing except being there
1135 DummyValue = class(AbstractValue)
1136
1137 function DummyValue.__init__(self, ...)
1138 AbstractValue.__init__(self, ...)
1139 self.template = "cbi/dvalue"
1140 self.value = nil
1141 end
1142
1143 function DummyValue.cfgvalue(self, section)
1144 local value
1145 if self.value then
1146 if type(self.value) == "function" then
1147 value = self:value(section)
1148 else
1149 value = self.value
1150 end
1151 else
1152 value = AbstractValue.cfgvalue(self, section)
1153 end
1154 return value
1155 end
1156
1157 function DummyValue.parse(self)
1158
1159 end
1160
1161
1162 --[[
1163 Flag - A flag being enabled or disabled
1164 ]]--
1165 Flag = class(AbstractValue)
1166
1167 function Flag.__init__(self, ...)
1168 AbstractValue.__init__(self, ...)
1169 self.template = "cbi/fvalue"
1170
1171 self.enabled = "1"
1172 self.disabled = "0"
1173 end
1174
1175 -- A flag can only have two states: set or unset
1176 function Flag.parse(self, section)
1177 local fvalue = self:formvalue(section)
1178
1179 if fvalue then
1180 fvalue = self.enabled
1181 else
1182 fvalue = self.disabled
1183 end
1184
1185 if fvalue == self.enabled or (not self.optional and not self.rmempty) then
1186 if not(fvalue == self:cfgvalue(section)) then
1187 self:write(section, fvalue)
1188 end
1189 else
1190 self:remove(section)
1191 end
1192 end
1193
1194
1195
1196 --[[
1197 ListValue - A one-line value predefined in a list
1198 widget: The widget that will be used (select, radio)
1199 ]]--
1200 ListValue = class(AbstractValue)
1201
1202 function ListValue.__init__(self, ...)
1203 AbstractValue.__init__(self, ...)
1204 self.template = "cbi/lvalue"
1205
1206 self.keylist = {}
1207 self.vallist = {}
1208 self.size = 1
1209 self.widget = "select"
1210 end
1211
1212 function ListValue.prepare(self, ...)
1213 AbstractValue.prepare(self, ...)
1214 if not self.override_scheme
1215 and self.map:get_scheme(self.section.sectiontype, self.option) then
1216 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1217 if self.value and vs.valuelist and not self.override_values then
1218 for k, v in ipairs(vs.valuelist) do
1219 local deps = {}
1220 if not self.override_dependencies
1221 and vs.enum_depends and vs.enum_depends[v.value] then
1222 for i, dep in ipairs(vs.enum_depends[v.value]) do
1223 table.insert(deps, _uvl_strip_remote_dependencies(dep))
1224 end
1225 end
1226 self:value(v.value, v.title or v.value, unpack(deps))
1227 end
1228 end
1229 end
1230 end
1231
1232 function ListValue.value(self, key, val, ...)
1233 if luci.util.contains(self.keylist, key) then
1234 return
1235 end
1236
1237 val = val or key
1238 table.insert(self.keylist, tostring(key))
1239 table.insert(self.vallist, tostring(val))
1240
1241 for i, deps in ipairs({...}) do
1242 table.insert(self.deps, {add = "-"..key, deps=deps})
1243 end
1244 end
1245
1246 function ListValue.validate(self, val)
1247 if luci.util.contains(self.keylist, val) then
1248 return val
1249 else
1250 return nil
1251 end
1252 end
1253
1254
1255
1256 --[[
1257 MultiValue - Multiple delimited values
1258 widget: The widget that will be used (select, checkbox)
1259 delimiter: The delimiter that will separate the values (default: " ")
1260 ]]--
1261 MultiValue = class(AbstractValue)
1262
1263 function MultiValue.__init__(self, ...)
1264 AbstractValue.__init__(self, ...)
1265 self.template = "cbi/mvalue"
1266
1267 self.keylist = {}
1268 self.vallist = {}
1269
1270 self.widget = "checkbox"
1271 self.delimiter = " "
1272 end
1273
1274 function MultiValue.render(self, ...)
1275 if self.widget == "select" and not self.size then
1276 self.size = #self.vallist
1277 end
1278
1279 AbstractValue.render(self, ...)
1280 end
1281
1282 function MultiValue.value(self, key, val)
1283 if luci.util.contains(self.keylist, key) then
1284 return
1285 end
1286
1287 val = val or key
1288 table.insert(self.keylist, tostring(key))
1289 table.insert(self.vallist, tostring(val))
1290 end
1291
1292 function MultiValue.valuelist(self, section)
1293 local val = self:cfgvalue(section)
1294
1295 if not(type(val) == "string") then
1296 return {}
1297 end
1298
1299 return luci.util.split(val, self.delimiter)
1300 end
1301
1302 function MultiValue.validate(self, val)
1303 val = (type(val) == "table") and val or {val}
1304
1305 local result
1306
1307 for i, value in ipairs(val) do
1308 if luci.util.contains(self.keylist, value) then
1309 result = result and (result .. self.delimiter .. value) or value
1310 end
1311 end
1312
1313 return result
1314 end
1315
1316
1317 StaticList = class(MultiValue)
1318
1319 function StaticList.__init__(self, ...)
1320 MultiValue.__init__(self, ...)
1321 self.cast = "table"
1322 self.valuelist = self.cfgvalue
1323
1324 if not self.override_scheme
1325 and self.map:get_scheme(self.section.sectiontype, self.option) then
1326 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1327 if self.value and vs.values and not self.override_values then
1328 for k, v in pairs(vs.values) do
1329 self:value(k, v)
1330 end
1331 end
1332 end
1333 end
1334
1335 function StaticList.validate(self, value)
1336 value = (type(value) == "table") and value or {value}
1337
1338 local valid = {}
1339 for i, v in ipairs(value) do
1340 if luci.util.contains(self.vallist, v) then
1341 table.insert(valid, v)
1342 end
1343 end
1344 return valid
1345 end
1346
1347
1348 DynamicList = class(AbstractValue)
1349
1350 function DynamicList.__init__(self, ...)
1351 AbstractValue.__init__(self, ...)
1352 self.template = "cbi/dynlist"
1353 self.cast = "table"
1354 self.keylist = {}
1355 self.vallist = {}
1356 end
1357
1358 function DynamicList.value(self, key, val)
1359 val = val or key
1360 table.insert(self.keylist, tostring(key))
1361 table.insert(self.vallist, tostring(val))
1362 end
1363
1364 function DynamicList.formvalue(self, section)
1365 local value = AbstractValue.formvalue(self, section)
1366 value = (type(value) == "table") and value or {value}
1367
1368 local valid = {}
1369 for i, v in ipairs(value) do
1370 if v and #v > 0
1371 and not luci.http.formvalue("cbi.rle."..section.."."..self.option.."."..i)
1372 and not luci.http.formvalue("cbi.rle."..section.."."..self.option.."."..i..".x") then
1373 table.insert(valid, v)
1374 end
1375 end
1376
1377 return valid
1378 end
1379
1380
1381 --[[
1382 TextValue - A multi-line value
1383 rows: Rows
1384 ]]--
1385 TextValue = class(AbstractValue)
1386
1387 function TextValue.__init__(self, ...)
1388 AbstractValue.__init__(self, ...)
1389 self.template = "cbi/tvalue"
1390 end
1391
1392 --[[
1393 Button
1394 ]]--
1395 Button = class(AbstractValue)
1396
1397 function Button.__init__(self, ...)
1398 AbstractValue.__init__(self, ...)
1399 self.template = "cbi/button"
1400 self.inputstyle = nil
1401 self.rmempty = true
1402 end
1403
1404
1405 FileUpload = class(AbstractValue)
1406
1407 function FileUpload.__init__(self, ...)
1408 AbstractValue.__init__(self, ...)
1409 self.template = "cbi/upload"
1410 if not self.map.upload_fields then
1411 self.map.upload_fields = { self }
1412 else
1413 self.map.upload_fields[#self.map.upload_fields+1] = self
1414 end
1415 end
1416
1417 function FileUpload.formcreated(self, section)
1418 return AbstractValue.formcreated(self, section) or
1419 luci.http.formvalue("cbi.rlf."..section.."."..self.option) or
1420 luci.http.formvalue("cbi.rlf."..section.."."..self.option..".x")
1421 end
1422
1423 function FileUpload.cfgvalue(self, section)
1424 local val = AbstractValue.cfgvalue(self, section)
1425 if val and luci.fs.access(val) then
1426 return val
1427 end
1428 return nil
1429 end
1430
1431 function FileUpload.formvalue(self, section)
1432 local val = AbstractValue.formvalue(self, section)
1433 if val then
1434 if not luci.http.formvalue("cbi.rlf."..section.."."..self.option) and
1435 not luci.http.formvalue("cbi.rlf."..section.."."..self.option..".x")
1436 then
1437 return val
1438 end
1439 luci.fs.unlink(val)
1440 self.value = nil
1441 end
1442 return nil
1443 end
1444
1445 function FileUpload.remove(self, section)
1446 local val = AbstractValue.formvalue(self, section)
1447 if val and luci.fs.access(val) then luci.fs.unlink(val) end
1448 return AbstractValue.remove(self, section)
1449 end
1450
1451
1452 FileBrowser = class(AbstractValue)
1453
1454 function FileBrowser.__init__(self, ...)
1455 AbstractValue.__init__(self, ...)
1456 self.template = "cbi/browser"
1457 end