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