f54d5ac9a42d3655d7874ce92c898250b02fd555
[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