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