Added missing value escaping
[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.model.uci")
33
34 local uci        = 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 CREATE_PREFIX = "cbi.cts."
43 REMOVE_PREFIX = "cbi.rts."
44
45 -- Loads a CBI map from given file, creating an environment and returns it
46 function load(cbimap, ...)
47         require("luci.fs")
48         require("luci.i18n")
49         require("luci.config")
50         require("luci.util")
51
52         local cbidir = luci.util.libpath() .. "/model/cbi/"
53         local func, err = loadfile(cbidir..cbimap..".lua")
54
55         if not func then
56                 return nil
57         end
58
59         luci.i18n.loadc("cbi")
60
61         luci.util.resfenv(func)
62         luci.util.updfenv(func, luci.cbi)
63         luci.util.extfenv(func, "translate", luci.i18n.translate)
64         luci.util.extfenv(func, "translatef", luci.i18n.translatef)
65         luci.util.extfenv(func, "arg", {...})
66
67         local maps = {func()}
68
69         for i, map in ipairs(maps) do
70                 if not instanceof(map, Node) then
71                         error("CBI map returns no valid map object!")
72                         return nil
73                 end
74         end
75
76         return maps
77 end
78
79 -- Node pseudo abstract class
80 Node = class()
81
82 function Node.__init__(self, title, description)
83         self.children = {}
84         self.title = title or ""
85         self.description = description or ""
86         self.template = "cbi/node"
87 end
88
89 -- i18n helper
90 function Node._i18n(self, config, section, option, title, description)
91
92         -- i18n loaded?
93         if type(luci.i18n) == "table" then
94
95                 local key = config:gsub("[^%w]+", "")
96
97                 if section then key = key .. "_" .. section:lower():gsub("[^%w]+", "") end
98                 if option  then key = key .. "_" .. option:lower():gsub("[^%w]+", "")  end
99
100                 self.title = title or luci.i18n.translate( key, option or section or config )
101                 self.description = description or luci.i18n.translate( key .. "_desc", "" )
102         end
103 end
104
105 -- Append child nodes
106 function Node.append(self, obj)
107         table.insert(self.children, obj)
108 end
109
110 -- Parse this node and its children
111 function Node.parse(self, ...)
112         for k, child in ipairs(self.children) do
113                 child:parse(...)
114         end
115 end
116
117 -- Render this node
118 function Node.render(self, scope)
119         scope = scope or {}
120         scope.self = self
121
122         luci.template.render(self.template, scope)
123 end
124
125 -- Render the children
126 function Node.render_children(self, ...)
127         for k, node in ipairs(self.children) do
128                 node:render(...)
129         end
130 end
131
132
133 --[[
134 A simple template element
135 ]]--
136 Template = class(Node)
137
138 function Template.__init__(self, template)
139         Node.__init__(self)
140         self.template = template
141 end
142
143 function Template.render(self)
144         luci.template.render(self.template, {self=self})
145 end
146
147
148 --[[
149 Map - A map describing a configuration file
150 ]]--
151 Map = class(Node)
152
153 function Map.__init__(self, config, ...)
154         Node.__init__(self, ...)
155         Node._i18n(self, config, nil, nil, ...)
156
157         self.config = config
158         self.parsechain = {self.config}
159         self.template = "cbi/map"
160         if not uci.load(self.config) then
161                 error("Unable to read UCI data: " .. self.config)
162         end
163 end
164
165
166 -- Chain foreign config
167 function Map.chain(self, config)
168         table.insert(self.parsechain, config)
169 end
170
171 -- Use optimized UCI writing
172 function Map.parse(self, ...)
173         Node.parse(self, ...)
174         for i, config in ipairs(self.parsechain) do
175                 uci.save(config)
176         end
177         if luci.http.formvalue("cbi.apply") then
178                 for i, config in ipairs(self.parsechain) do
179                         uci.commit(config)
180                         if luci.config.uci_oncommit and luci.config.uci_oncommit[config] then
181                                 luci.util.exec(luci.config.uci_oncommit[config])
182                         end
183
184                         -- Refresh data because commit changes section names
185                         uci.unload(config)
186                         uci.load(config)
187                 end
188
189                 -- Reparse sections
190                 Node.parse(self, ...)
191
192         end
193         for i, config in ipairs(self.parsechain) do
194                 uci.unload(config)
195         end
196 end
197
198 -- Creates a child section
199 function Map.section(self, class, ...)
200         if instanceof(class, AbstractSection) then
201                 local obj  = class(self, ...)
202                 self:append(obj)
203                 return obj
204         else
205                 error("class must be a descendent of AbstractSection")
206         end
207 end
208
209 -- UCI add
210 function Map.add(self, sectiontype)
211         return uci.add(self.config, sectiontype)
212 end
213
214 -- UCI set
215 function Map.set(self, section, option, value)
216         if option then
217                 return uci.set(self.config, section, option, value)
218         else
219                 return uci.set(self.config, section, value)
220         end
221 end
222
223 -- UCI del
224 function Map.del(self, section, option)
225         if option then
226                 return uci.delete(self.config, section, option)
227         else
228                 return uci.delete(self.config, section)
229         end
230 end
231
232 -- UCI get
233 function Map.get(self, section, option)
234         if not section then
235                 return uci.get_all(self.config)
236         elseif option then
237                 return uci.get(self.config, section, option)
238         else
239                 return uci.get_all(self.config, section)
240         end
241 end
242
243 -- UCI stateget
244 function Map.stateget(self, section, option)
245         return uci.get_statevalue(self.config, section, option)
246 end
247
248
249 --[[
250 Page - A simple node
251 ]]--
252
253 Page = class(Node)
254 Page.__init__ = Node.__init__
255 Page.parse    = function() end
256
257
258 --[[
259 SimpleForm - A Simple non-UCI form
260 ]]--
261 SimpleForm = class(Node)
262
263 function SimpleForm.__init__(self, config, title, description, data)
264         Node.__init__(self, title, description)
265         self.config = config
266         self.data = data or {}
267         self.template = "cbi/simpleform"
268         self.dorender = true
269 end
270
271 function SimpleForm.parse(self, ...)
272         if luci.http.formvalue("cbi.submit") then
273                 Node.parse(self, 1, ...)
274         end
275                 
276         local valid = true
277         for k, j in ipairs(self.children) do 
278                 for i, v in ipairs(j.children) do
279                         valid = valid 
280                          and (not v.tag_missing or not v.tag_missing[1])
281                          and (not v.tag_invalid or not v.tag_invalid[1])
282                 end
283         end
284         
285         local state = 
286                 not luci.http.formvalue("cbi.submit") and 0
287                 or valid and 1
288                 or -1
289
290         self.dorender = self:handle(state, self.data) ~= false
291 end
292
293 function SimpleForm.render(self, ...)
294         if self.dorender then
295                 Node.render(self, ...)
296         end
297 end
298
299 function SimpleForm.section(self, class, ...)
300         if instanceof(class, AbstractSection) then
301                 local obj  = class(self, ...)
302                 self:append(obj)
303                 return obj
304         else
305                 error("class must be a descendent of AbstractSection")
306         end
307 end
308
309 -- Creates a child field
310 function SimpleForm.field(self, class, ...)
311         local section
312         for k, v in ipairs(self.children) do
313                 if instanceof(v, SimpleSection) then
314                         section = v
315                         break
316                 end
317         end
318         if not section then
319                 section = self:section(SimpleSection)
320         end
321         
322         if instanceof(class, AbstractValue) then
323                 local obj  = class(self, ...)
324                 obj.track_missing = true
325                 section:append(obj)
326                 return obj
327         else
328                 error("class must be a descendent of AbstractValue")
329         end
330 end
331
332 function SimpleForm.set(self, section, option, value)
333         self.data[option] = value
334 end
335
336
337 function SimpleForm.del(self, section, option)
338         self.data[option] = nil
339 end
340
341
342 function SimpleForm.get(self, section, option)
343         return self.data[option]
344 end
345
346
347
348 --[[
349 AbstractSection
350 ]]--
351 AbstractSection = class(Node)
352
353 function AbstractSection.__init__(self, map, sectiontype, ...)
354         Node.__init__(self, ...)
355         self.sectiontype = sectiontype
356         self.map = map
357         self.config = map.config
358         self.optionals = {}
359         self.defaults = {}
360
361         self.optional = true
362         self.addremove = false
363         self.dynamic = false
364 end
365
366 -- Appends a new option
367 function AbstractSection.option(self, class, option, ...)
368         if instanceof(class, AbstractValue) then
369                 local obj  = class(self.map, option, ...)
370
371                 Node._i18n(obj, self.config, self.section or self.sectiontype, option, ...)
372
373                 self:append(obj)
374                 return obj
375         else
376                 error("class must be a descendent of AbstractValue")
377         end
378 end
379
380 -- Parse optional options
381 function AbstractSection.parse_optionals(self, section)
382         if not self.optional then
383                 return
384         end
385
386         self.optionals[section] = {}
387
388         local field = luci.http.formvalue("cbi.opt."..self.config.."."..section)
389         for k,v in ipairs(self.children) do
390                 if v.optional and not v:cfgvalue(section) then
391                         if field == v.option then
392                                 field = nil
393                         else
394                                 table.insert(self.optionals[section], v)
395                         end
396                 end
397         end
398
399         if field and #field > 0 and self.dynamic then
400                 self:add_dynamic(field)
401         end
402 end
403
404 -- Add a dynamic option
405 function AbstractSection.add_dynamic(self, field, optional)
406         local o = self:option(Value, field, field)
407         o.optional = optional
408 end
409
410 -- Parse all dynamic options
411 function AbstractSection.parse_dynamic(self, section)
412         if not self.dynamic then
413                 return
414         end
415
416         local arr  = luci.util.clone(self:cfgvalue(section))
417         local form = luci.http.formvaluetable("cbid."..self.config.."."..section)
418         for k, v in pairs(form) do
419                 arr[k] = v
420         end
421
422         for key,val in pairs(arr) do
423                 local create = true
424
425                 for i,c in ipairs(self.children) do
426                         if c.option == key then
427                                 create = false
428                         end
429                 end
430
431                 if create and key:sub(1, 1) ~= "." then
432                         self:add_dynamic(key, true)
433                 end
434         end
435 end
436
437 -- Returns the section's UCI table
438 function AbstractSection.cfgvalue(self, section)
439         return self.map:get(section)
440 end
441
442 -- Removes the section
443 function AbstractSection.remove(self, section)
444         return self.map:del(section)
445 end
446
447 -- Creates the section
448 function AbstractSection.create(self, section)
449         local stat
450         
451         if section then
452                 stat = self.map:set(section, nil, self.sectiontype)
453         else
454                 section = self.map:add(self.sectiontype)
455                 stat = section
456         end
457
458         if stat then
459                 for k,v in pairs(self.children) do
460                         if v.default then
461                                 self.map:set(section, v.option, v.default)
462                         end
463                 end
464
465                 for k,v in pairs(self.defaults) do
466                         self.map:set(section, k, v)
467                 end
468         end
469
470         return stat
471 end
472
473
474 SimpleSection = class(AbstractSection)
475
476 function SimpleSection.__init__(self, form, ...)
477         AbstractSection.__init__(self, form, nil, ...)
478         self.template = "cbi/nullsection"
479 end
480
481
482 Table = class(AbstractSection)
483
484 function Table.__init__(self, form, data, ...)
485         local datasource = {}
486         self.data = data
487         
488         function datasource.get(self, section, option)
489                 return data[option]
490         end
491         
492         AbstractSection.__init__(self, datasource, nil, ...)
493 end
494
495 function Table.cfgsections(self)
496         local sections = {}
497         
498         for i, v in pairs(self.data) do
499                 table.insert(sections, i)
500         end
501         
502         return sections
503 end
504
505
506
507 --[[
508 NamedSection - A fixed configuration section defined by its name
509 ]]--
510 NamedSection = class(AbstractSection)
511
512 function NamedSection.__init__(self, map, section, type, ...)
513         AbstractSection.__init__(self, map, type, ...)
514         Node._i18n(self, map.config, section, nil, ...)
515
516         self.template = "cbi/nsection"
517         self.section = section
518         self.addremove = false
519 end
520
521 function NamedSection.parse(self)
522         local s = self.section
523         local active = self:cfgvalue(s)
524
525
526         if self.addremove then
527                 local path = self.config.."."..s
528                 if active then -- Remove the section
529                         if luci.http.formvalue("cbi.rns."..path) and self:remove(s) then
530                                 return
531                         end
532                 else           -- Create and apply default values
533                         if luci.http.formvalue("cbi.cns."..path) then
534                                 self:create(s)
535                                 return
536                         end
537                 end
538         end
539
540         if active then
541                 AbstractSection.parse_dynamic(self, s)
542                 if luci.http.formvalue("cbi.submit") then
543                         Node.parse(self, s)
544                 end
545                 AbstractSection.parse_optionals(self, s)
546         end
547 end
548
549
550 --[[
551 TypedSection - A (set of) configuration section(s) defined by the type
552         addremove:      Defines whether the user can add/remove sections of this type
553         anonymous:  Allow creating anonymous sections
554         validate:       a validation function returning nil if the section is invalid
555 ]]--
556 TypedSection = class(AbstractSection)
557
558 function TypedSection.__init__(self, map, type, ...)
559         AbstractSection.__init__(self, map, type, ...)
560         Node._i18n(self, map.config, type, nil, ...)
561
562         self.template  = "cbi/tsection"
563         self.deps = {}
564
565         self.anonymous = false
566 end
567
568 -- Return all matching UCI sections for this TypedSection
569 function TypedSection.cfgsections(self)
570         local sections = {}
571         uci.foreach(self.map.config, self.sectiontype,
572                 function (section)
573                         if self:checkscope(section[".name"]) then
574                                 table.insert(sections, section[".name"])
575                         end
576                 end)
577
578         return sections
579 end
580
581 -- Limits scope to sections that have certain option => value pairs
582 function TypedSection.depends(self, option, value)
583         table.insert(self.deps, {option=option, value=value})
584 end
585
586 function TypedSection.parse(self)
587         if self.addremove then
588                 -- Create
589                 local crval = CREATE_PREFIX .. self.config .. "." .. self.sectiontype
590                 local name  = luci.http.formvalue(crval)
591                 if self.anonymous then
592                         if name then
593                                 self:create()
594                         end
595                 else
596                         if name then
597                                 -- Ignore if it already exists
598                                 if self:cfgvalue(name) then
599                                         name = nil;
600                                 end
601
602                                 name = self:checkscope(name)
603
604                                 if not name then
605                                         self.err_invalid = true
606                                 end
607
608                                 if name and name:len() > 0 then
609                                         self:create(name)
610                                 end
611                         end
612                 end
613
614                 -- Remove
615                 crval = REMOVE_PREFIX .. self.config
616                 name = luci.http.formvaluetable(crval)
617                 for k,v in pairs(name) do
618                         if self:cfgvalue(k) and self:checkscope(k) then
619                                 self:remove(k)
620                         end
621                 end
622         end
623
624         for i, k in ipairs(self:cfgsections()) do
625                 AbstractSection.parse_dynamic(self, k)
626                 if luci.http.formvalue("cbi.submit") then
627                         Node.parse(self, k)
628                 end
629                 AbstractSection.parse_optionals(self, k)
630         end
631 end
632
633 -- Verifies scope of sections
634 function TypedSection.checkscope(self, section)
635         -- Check if we are not excluded
636         if self.filter and not self:filter(section) then
637                 return nil
638         end
639
640         -- Check if at least one dependency is met
641         if #self.deps > 0 and self:cfgvalue(section) then
642                 local stat = false
643
644                 for k, v in ipairs(self.deps) do
645                         if self:cfgvalue(section)[v.option] == v.value then
646                                 stat = true
647                         end
648                 end
649
650                 if not stat then
651                         return nil
652                 end
653         end
654
655         return self:validate(section)
656 end
657
658
659 -- Dummy validate function
660 function TypedSection.validate(self, section)
661         return section
662 end
663
664
665 --[[
666 AbstractValue - An abstract Value Type
667         null:           Value can be empty
668         valid:          A function returning the value if it is valid otherwise nil
669         depends:        A table of option => value pairs of which one must be true
670         default:        The default value
671         size:           The size of the input fields
672         rmempty:        Unset value if empty
673         optional:       This value is optional (see AbstractSection.optionals)
674 ]]--
675 AbstractValue = class(Node)
676
677 function AbstractValue.__init__(self, map, option, ...)
678         Node.__init__(self, ...)
679         self.option = option
680         self.map    = map
681         self.config = map.config
682         self.tag_invalid = {}
683         self.tag_missing = {}
684         self.deps = {}
685
686         self.track_missing = false
687         self.rmempty   = false
688         self.default   = nil
689         self.size      = nil
690         self.optional  = false
691         self.stateful  = false
692 end
693
694 -- Add a dependencie to another section field
695 function AbstractValue.depends(self, field, value)
696         table.insert(self.deps, {field=field, value=value})
697 end
698
699 -- Return whether this object should be created
700 function AbstractValue.formcreated(self, section)
701         local key = "cbi.opt."..self.config.."."..section
702         return (luci.http.formvalue(key) == self.option)
703 end
704
705 -- Returns the formvalue for this object
706 function AbstractValue.formvalue(self, section)
707         local key = "cbid."..self.map.config.."."..section.."."..self.option
708         return luci.http.formvalue(key)
709 end
710
711 function AbstractValue.additional(self, value)
712         self.optional = value
713 end
714
715 function AbstractValue.mandatory(self, value)
716         self.rmempty = not value
717 end
718
719 function AbstractValue.parse(self, section)
720         local fvalue = self:formvalue(section)
721         local cvalue = self:cfgvalue(section)
722
723         if fvalue and fvalue ~= "" then -- If we have a form value, write it to UCI
724                 fvalue = self:transform(self:validate(fvalue, section))
725                 if not fvalue then
726                         self.tag_invalid[section] = true
727                 end
728                 if fvalue and not (fvalue == cvalue) then
729                         self:write(section, fvalue)
730                 end
731         else                                                    -- Unset the UCI or error
732                 if self.rmempty or self.optional then
733                         self:remove(section)
734                 elseif self.track_missing and (not fvalue or fvalue ~= cvalue) then
735                         self.tag_missing[section] = true
736                 end
737         end
738 end
739
740 -- Render if this value exists or if it is mandatory
741 function AbstractValue.render(self, s, scope)
742         if not self.optional or self:cfgvalue(s) or self:formcreated(s) then
743                 scope = scope or {}
744                 scope.section = s
745                 scope.cbid    = "cbid." .. self.config ..
746                                 "."     .. s           ..
747                                                 "."     .. self.option
748
749                 scope.ifattr = function(cond,key,val)
750                         if cond then
751                                 return string.format(
752                                         ' %s="%s"', tostring(key),
753                                         luci.util.pcdata(tostring( val
754                                          or scope[key]
755                                          or (type(self[key]) ~= "function" and self[key])
756                                          or "" ))
757                                 )
758                         else
759                                 return ''
760                         end
761                 end
762
763                 scope.attr = function(...)
764                         return scope.ifattr( true, ... )
765                 end
766
767                 Node.render(self, scope)
768         end
769 end
770
771 -- Return the UCI value of this object
772 function AbstractValue.cfgvalue(self, section)
773         return self.stateful
774          and self.map:stateget(section, self.option)
775          or  self.map:get(section, self.option)
776 end
777
778 -- Validate the form value
779 function AbstractValue.validate(self, value)
780         return value
781 end
782
783 AbstractValue.transform = AbstractValue.validate
784
785
786 -- Write to UCI
787 function AbstractValue.write(self, section, value)
788         return self.map:set(section, self.option, value)
789 end
790
791 -- Remove from UCI
792 function AbstractValue.remove(self, section)
793         return self.map:del(section, self.option)
794 end
795
796
797
798
799 --[[
800 Value - A one-line value
801         maxlength:      The maximum length
802 ]]--
803 Value = class(AbstractValue)
804
805 function Value.__init__(self, ...)
806         AbstractValue.__init__(self, ...)
807         self.template  = "cbi/value"
808         self.keylist = {}
809         self.vallist = {}
810 end
811
812 function Value.value(self, key, val)
813         val = val or key
814         table.insert(self.keylist, tostring(key))
815         table.insert(self.vallist, tostring(val))
816 end
817
818
819 -- DummyValue - This does nothing except being there
820 DummyValue = class(AbstractValue)
821
822 function DummyValue.__init__(self, map, ...)
823         AbstractValue.__init__(self, map, ...)
824         self.template = "cbi/dvalue"
825         self.value = nil
826 end
827
828 function DummyValue.parse(self)
829
830 end
831
832
833 --[[
834 Flag - A flag being enabled or disabled
835 ]]--
836 Flag = class(AbstractValue)
837
838 function Flag.__init__(self, ...)
839         AbstractValue.__init__(self, ...)
840         self.template  = "cbi/fvalue"
841
842         self.enabled = "1"
843         self.disabled = "0"
844 end
845
846 -- A flag can only have two states: set or unset
847 function Flag.parse(self, section)
848         local fvalue = self:formvalue(section)
849
850         if fvalue then
851                 fvalue = self.enabled
852         else
853                 fvalue = self.disabled
854         end
855
856         if fvalue == self.enabled or (not self.optional and not self.rmempty) then
857                 if not(fvalue == self:cfgvalue(section)) then
858                         self:write(section, fvalue)
859                 end
860         else
861                 self:remove(section)
862         end
863 end
864
865
866
867 --[[
868 ListValue - A one-line value predefined in a list
869         widget: The widget that will be used (select, radio)
870 ]]--
871 ListValue = class(AbstractValue)
872
873 function ListValue.__init__(self, ...)
874         AbstractValue.__init__(self, ...)
875         self.template  = "cbi/lvalue"
876         self.keylist = {}
877         self.vallist = {}
878
879         self.size   = 1
880         self.widget = "select"
881 end
882
883 function ListValue.value(self, key, val)
884         val = val or key
885         table.insert(self.keylist, tostring(key))
886         table.insert(self.vallist, tostring(val))
887 end
888
889 function ListValue.validate(self, val)
890         if luci.util.contains(self.keylist, val) then
891                 return val
892         else
893                 return nil
894         end
895 end
896
897
898
899 --[[
900 MultiValue - Multiple delimited values
901         widget: The widget that will be used (select, checkbox)
902         delimiter: The delimiter that will separate the values (default: " ")
903 ]]--
904 MultiValue = class(AbstractValue)
905
906 function MultiValue.__init__(self, ...)
907         AbstractValue.__init__(self, ...)
908         self.template = "cbi/mvalue"
909         self.keylist = {}
910         self.vallist = {}
911
912         self.widget = "checkbox"
913         self.delimiter = " "
914 end
915
916 function MultiValue.render(self, ...)
917         if self.widget == "select" and not self.size then
918                 self.size = #self.vallist
919         end
920
921         AbstractValue.render(self, ...)
922 end
923
924 function MultiValue.value(self, key, val)
925         val = val or key
926         table.insert(self.keylist, tostring(key))
927         table.insert(self.vallist, tostring(val))
928 end
929
930 function MultiValue.valuelist(self, section)
931         local val = self:cfgvalue(section)
932
933         if not(type(val) == "string") then
934                 return {}
935         end
936
937         return luci.util.split(val, self.delimiter)
938 end
939
940 function MultiValue.validate(self, val)
941         val = (type(val) == "table") and val or {val}
942
943         local result
944
945         for i, value in ipairs(val) do
946                 if luci.util.contains(self.keylist, value) then
947                         result = result and (result .. self.delimiter .. value) or value
948                 end
949         end
950
951         return result
952 end
953
954 --[[
955 TextValue - A multi-line value
956         rows:   Rows
957 ]]--
958 TextValue = class(AbstractValue)
959
960 function TextValue.__init__(self, ...)
961         AbstractValue.__init__(self, ...)
962         self.template  = "cbi/tvalue"
963 end