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