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