2 LuCI - Configuration Bind Interface
5 Offers an interface for binding configuration values to certain
6 data types. Supports value and range validation and basic dependencies.
12 Copyright 2008 Steven Barth <steven@midlink.org>
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
18 http://www.apache.org/licenses/LICENSE-2.0
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.
27 module("luci.cbi", package.seeall)
29 require("luci.template")
34 local uci = require("luci.model.uci")
35 local class = luci.util.class
36 local instanceof = luci.util.instanceof
44 CREATE_PREFIX = "cbi.cts."
45 REMOVE_PREFIX = "cbi.rts."
47 -- Loads a CBI map from given file, creating an environment and returns it
48 function load(cbimap, ...)
51 require("luci.config")
54 local cbidir = luci.util.libpath() .. "/model/cbi/"
55 local func, err = loadfile(cbidir..cbimap..".lua")
61 luci.i18n.loadc("cbi")
63 luci.util.resfenv(func)
64 luci.util.updfenv(func, luci.cbi)
65 luci.util.extfenv(func, "translate", luci.i18n.translate)
66 luci.util.extfenv(func, "translatef", luci.i18n.translatef)
67 luci.util.extfenv(func, "arg", {...})
71 for i, map in ipairs(maps) do
72 if not instanceof(map, Node) then
73 error("CBI map returns no valid map object!")
82 function _uvl_strip_remote_dependencies(deps)
85 for k, v in pairs(deps) do
86 k = k:gsub("%$config%.%$section%.", "")
87 if k:match("^[%w_]+$") and type(v) == "string" then
96 -- Node pseudo abstract class
99 function Node.__init__(self, title, description)
101 self.title = title or ""
102 self.description = description or ""
103 self.template = "cbi/node"
107 function Node._i18n(self, config, section, option, title, description)
110 if type(luci.i18n) == "table" then
112 local key = config and config:gsub("[^%w]+", "") or ""
114 if section then key = key .. "_" .. section:lower():gsub("[^%w]+", "") end
115 if option then key = key .. "_" .. tostring(option):lower():gsub("[^%w]+", "") end
117 self.title = title or luci.i18n.translate( key, option or section or config )
118 self.description = description or luci.i18n.translate( key .. "_desc", "" )
122 -- Append child nodes
123 function Node.append(self, obj)
124 table.insert(self.children, obj)
127 -- Parse this node and its children
128 function Node.parse(self, ...)
129 for k, child in ipairs(self.children) do
135 function Node.render(self, scope)
139 luci.template.render(self.template, scope)
142 -- Render the children
143 function Node.render_children(self, ...)
144 for k, node in ipairs(self.children) do
151 A simple template element
153 Template = class(Node)
155 function Template.__init__(self, template)
157 self.template = template
160 function Template.render(self)
161 luci.template.render(self.template, {self=self})
166 Map - A map describing a configuration file
170 function Map.__init__(self, config, ...)
171 Node.__init__(self, ...)
172 Node._i18n(self, config, nil, nil, ...)
175 self.parsechain = {self.config}
176 self.template = "cbi/map"
177 self.uci = uci.cursor()
179 if not self.uci:load(self.config) then
180 error("Unable to read UCI data: " .. self.config)
183 self.validator = luci.uvl.UVL()
184 self.scheme = self.validator:get_scheme(self.config)
188 function Map.get_scheme(self, sectiontype, option)
190 return self.scheme and self.scheme.sections[sectiontype]
192 return self.scheme and self.scheme.variables[sectiontype]
193 and self.scheme.variables[sectiontype][option]
198 -- Chain foreign config
199 function Map.chain(self, config)
200 table.insert(self.parsechain, config)
203 -- Use optimized UCI writing
204 function Map.parse(self, ...)
205 Node.parse(self, ...)
208 for i, config in ipairs(self.parsechain) do
209 self.uci:save(config)
211 if luci.http.formvalue("cbi.apply") then
212 for i, config in ipairs(self.parsechain) do
213 self.uci:commit(config)
214 self.uci:apply(config)
216 -- Refresh data because commit changes section names
217 self.uci:load(config)
221 Node.parse(self, ...)
224 for i, config in ipairs(self.parsechain) do
225 self.uci:unload(config)
230 -- Creates a child section
231 function Map.section(self, class, ...)
232 if instanceof(class, AbstractSection) then
233 local obj = class(self, ...)
237 error("class must be a descendent of AbstractSection")
242 function Map.add(self, sectiontype)
243 return self.uci:add(self.config, sectiontype)
247 function Map.set(self, section, option, value)
249 return self.uci:set(self.config, section, option, value)
251 return self.uci:set(self.config, section, value)
256 function Map.del(self, section, option)
258 return self.uci:delete(self.config, section, option)
260 return self.uci:delete(self.config, section)
265 function Map.get(self, section, option)
267 return self.uci:get_all(self.config)
269 return self.uci:get(self.config, section, option)
271 return self.uci:get_all(self.config, section)
281 Page.__init__ = Node.__init__
282 Page.parse = function() end
286 SimpleForm - A Simple non-UCI form
288 SimpleForm = class(Node)
290 function SimpleForm.__init__(self, config, title, description, data)
291 Node.__init__(self, title, description)
293 self.data = data or {}
294 self.template = "cbi/simpleform"
298 function SimpleForm.parse(self, ...)
299 if luci.http.formvalue("cbi.submit") then
300 Node.parse(self, 1, ...)
304 for k, j in ipairs(self.children) do
305 for i, v in ipairs(j.children) do
307 and (not v.tag_missing or not v.tag_missing[1])
308 and (not v.tag_invalid or not v.tag_invalid[1])
313 not luci.http.formvalue("cbi.submit") and 0
317 self.dorender = not self.handle or self:handle(state, self.data) ~= false
320 function SimpleForm.render(self, ...)
321 if self.dorender then
322 Node.render(self, ...)
326 function SimpleForm.section(self, class, ...)
327 if instanceof(class, AbstractSection) then
328 local obj = class(self, ...)
332 error("class must be a descendent of AbstractSection")
336 -- Creates a child field
337 function SimpleForm.field(self, class, ...)
339 for k, v in ipairs(self.children) do
340 if instanceof(v, SimpleSection) then
346 section = self:section(SimpleSection)
349 if instanceof(class, AbstractValue) then
350 local obj = class(self, section, ...)
351 obj.track_missing = true
355 error("class must be a descendent of AbstractValue")
359 function SimpleForm.set(self, section, option, value)
360 self.data[option] = value
364 function SimpleForm.del(self, section, option)
365 self.data[option] = nil
369 function SimpleForm.get(self, section, option)
370 return self.data[option]
374 function SimpleForm.get_scheme()
383 AbstractSection = class(Node)
385 function AbstractSection.__init__(self, map, sectiontype, ...)
386 Node.__init__(self, ...)
387 self.sectiontype = sectiontype
389 self.config = map.config
394 self.tag_invalid = {}
395 self.tag_deperror = {}
398 self.addremove = false
402 -- Appends a new option
403 function AbstractSection.option(self, class, option, ...)
404 -- Autodetect from UVL
405 if class == true and self.map:get_scheme(self.sectiontype, option) then
406 local vs = self.map:get_scheme(self.sectiontype, option)
407 if vs.type == "boolean" then
409 elseif vs.type == "list" then
411 elseif vs.type == "enum" or vs.type == "reference" then
418 if instanceof(class, AbstractValue) then
419 local obj = class(self.map, self, option, ...)
421 Node._i18n(obj, self.config, self.section or self.sectiontype, option, ...)
424 self.fields[option] = obj
426 elseif class == true then
427 error("No valid class was given and autodetection failed.")
429 error("class must be a descendant of AbstractValue")
433 -- Parse optional options
434 function AbstractSection.parse_optionals(self, section)
435 if not self.optional then
439 self.optionals[section] = {}
441 local field = luci.http.formvalue("cbi.opt."..self.config.."."..section)
442 for k,v in ipairs(self.children) do
443 if v.optional and not v:cfgvalue(section) then
444 if field == v.option then
447 table.insert(self.optionals[section], v)
452 if field and #field > 0 and self.dynamic then
453 self:add_dynamic(field)
457 -- Add a dynamic option
458 function AbstractSection.add_dynamic(self, field, optional)
459 local o = self:option(Value, field, field)
460 o.optional = optional
463 -- Parse all dynamic options
464 function AbstractSection.parse_dynamic(self, section)
465 if not self.dynamic then
469 local arr = luci.util.clone(self:cfgvalue(section))
470 local form = luci.http.formvaluetable("cbid."..self.config.."."..section)
471 for k, v in pairs(form) do
475 for key,val in pairs(arr) do
478 for i,c in ipairs(self.children) do
479 if c.option == key then
484 if create and key:sub(1, 1) ~= "." then
485 self:add_dynamic(key, true)
490 -- Returns the section's UCI table
491 function AbstractSection.cfgvalue(self, section)
492 return self.map:get(section)
495 -- Removes the section
496 function AbstractSection.remove(self, section)
497 return self.map:del(section)
500 -- Creates the section
501 function AbstractSection.create(self, section)
505 stat = self.map:set(section, nil, self.sectiontype)
507 section = self.map:add(self.sectiontype)
512 for k,v in pairs(self.children) do
514 self.map:set(section, v.option, v.default)
518 for k,v in pairs(self.defaults) do
519 self.map:set(section, k, v)
527 SimpleSection = class(AbstractSection)
529 function SimpleSection.__init__(self, form, ...)
530 AbstractSection.__init__(self, form, nil, ...)
531 self.template = "cbi/nullsection"
535 Table = class(AbstractSection)
537 function Table.__init__(self, form, data, ...)
538 local datasource = {}
539 datasource.config = "table"
542 function datasource.get(self, section, option)
543 return data[section] and data[section][option]
546 function datasource.del(...)
550 function datasource.get_scheme()
554 AbstractSection.__init__(self, datasource, "table", ...)
555 self.template = "cbi/tblsection"
556 self.rowcolors = true
557 self.anonymous = true
560 function Table.parse(self)
561 for i, k in ipairs(self:cfgsections()) do
562 if luci.http.formvalue("cbi.submit") then
568 function Table.cfgsections(self)
571 for i, v in luci.util.kspairs(self.data) do
572 table.insert(sections, i)
581 NamedSection - A fixed configuration section defined by its name
583 NamedSection = class(AbstractSection)
585 function NamedSection.__init__(self, map, section, stype, ...)
586 AbstractSection.__init__(self, map, stype, ...)
587 Node._i18n(self, map.config, section, nil, ...)
590 self.addremove = false
592 -- Use defaults from UVL
593 if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
594 local vs = self.map:get_scheme(self.sectiontype)
595 self.addremove = not vs.unique and not vs.required
596 self.dynamic = vs.dynamic
597 self.title = self.title or vs.title
598 self.description = self.description or vs.descr
601 self.template = "cbi/nsection"
602 self.section = section
605 function NamedSection.parse(self)
606 local s = self.section
607 local active = self:cfgvalue(s)
609 if self.addremove then
610 local path = self.config.."."..s
611 if active then -- Remove the section
612 if luci.http.formvalue("cbi.rns."..path) and self:remove(s) then
615 else -- Create and apply default values
616 if luci.http.formvalue("cbi.cns."..path) then
624 AbstractSection.parse_dynamic(self, s)
625 if luci.http.formvalue("cbi.submit") then
628 if not self.override_scheme and self.map.scheme then
629 local co = self.map:get()
630 local stat, err = self.map.validator:validate_section(self.config, s, co)
632 --self.map.save = false
633 if err.code == luci.uvl.errors.ERR_DEPENDENCY then
634 self.tag_deperror[s] = true
636 self.tag_invalid[s] = true
638 for i, v in ipairs(err.childs) do
639 if v.option and self.fields[v.option] then
640 if v.code == luci.uvl.errors.ERR_DEPENDENCY then
641 self.fields[v.option].tag_reqerror[s] = true
642 elseif v.code == luci.uvl.errors.ERR_OPTION then
643 self.fields[v.option].tag_invalid[s] = true
650 AbstractSection.parse_optionals(self, s)
656 TypedSection - A (set of) configuration section(s) defined by the type
657 addremove: Defines whether the user can add/remove sections of this type
658 anonymous: Allow creating anonymous sections
659 validate: a validation function returning nil if the section is invalid
661 TypedSection = class(AbstractSection)
663 function TypedSection.__init__(self, map, type, ...)
664 AbstractSection.__init__(self, map, type, ...)
665 Node._i18n(self, map.config, type, nil, ...)
667 self.template = "cbi/tsection"
669 self.anonymous = false
671 -- Use defaults from UVL
672 if not self.override_scheme and self.map:get_scheme(self.sectiontype) then
673 local vs = self.map:get_scheme(self.sectiontype)
674 self.addremove = not vs.unique and not vs.required
675 self.dynamic = vs.dynamic
676 self.anonymous = not vs.named
677 self.title = self.title or vs.title
678 self.description = self.description or vs.descr
682 -- Return all matching UCI sections for this TypedSection
683 function TypedSection.cfgsections(self)
685 self.map.uci:foreach(self.map.config, self.sectiontype,
687 if self:checkscope(section[".name"]) then
688 table.insert(sections, section[".name"])
695 -- Limits scope to sections that have certain option => value pairs
696 function TypedSection.depends(self, option, value)
697 table.insert(self.deps, {option=option, value=value})
700 function TypedSection.parse(self)
701 if self.addremove then
703 local crval = CREATE_PREFIX .. self.config .. "." .. self.sectiontype
704 local name = luci.http.formvalue(crval)
705 if self.anonymous then
711 -- Ignore if it already exists
712 if self:cfgvalue(name) then
716 name = self:checkscope(name)
719 self.err_invalid = true
722 if name and #name > 0 then
729 crval = REMOVE_PREFIX .. self.config
730 name = luci.http.formvaluetable(crval)
731 for k,v in pairs(name) do
732 if self:cfgvalue(k) and self:checkscope(k) then
739 for i, k in ipairs(self:cfgsections()) do
740 AbstractSection.parse_dynamic(self, k)
741 if luci.http.formvalue("cbi.submit") then
744 if not self.override_scheme and self.map.scheme then
745 local co = self.map:get()
746 local stat, err = self.map.validator:validate_section(self.config, k, co)
748 --self.map.save = false
749 if err.code == luci.uvl.errors.ERR_DEPENDENCY then
750 self.tag_deperror[k] = true
752 self.tag_invalid[k] = true
754 for i, v in ipairs(err.childs) do
755 if v.option and self.fields[v.option] then
756 if v.code == luci.uvl.errors.ERR_DEPENDENCY then
757 self.fields[v.option].tag_reqerror[k] = true
758 elseif v.code == luci.uvl.errors.ERR_OPTION then
759 self.fields[v.option].tag_invalid[k] = true
766 AbstractSection.parse_optionals(self, k)
770 -- Verifies scope of sections
771 function TypedSection.checkscope(self, section)
772 -- Check if we are not excluded
773 if self.filter and not self:filter(section) then
777 -- Check if at least one dependency is met
778 if #self.deps > 0 and self:cfgvalue(section) then
781 for k, v in ipairs(self.deps) do
782 if self:cfgvalue(section)[v.option] == v.value then
792 return self:validate(section)
796 -- Dummy validate function
797 function TypedSection.validate(self, section)
803 AbstractValue - An abstract Value Type
804 null: Value can be empty
805 valid: A function returning the value if it is valid otherwise nil
806 depends: A table of option => value pairs of which one must be true
807 default: The default value
808 size: The size of the input fields
809 rmempty: Unset value if empty
810 optional: This value is optional (see AbstractSection.optionals)
812 AbstractValue = class(Node)
814 function AbstractValue.__init__(self, map, section, option, ...)
815 Node.__init__(self, ...)
816 self.section = section
819 self.config = map.config
820 self.tag_invalid = {}
821 self.tag_missing = {}
822 self.tag_reqerror = {}
827 self.track_missing = false
831 self.optional = false
833 -- Use defaults from UVL
834 if not self.override_scheme
835 and self.map:get_scheme(self.section.sectiontype, self.option) then
836 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
837 self.rmempty = not vs.required
838 self.cast = (vs.type == "list") and "list" or "string"
839 self.title = self.title or vs.title
840 self.description = self.description or vs.descr
842 if vs.depends and not self.override_dependencies then
843 for i, deps in ipairs(vs.depends) do
844 deps = _uvl_strip_remote_dependencies(deps)
853 -- Add a dependencie to another section field
854 function AbstractValue.depends(self, field, value)
856 if type(field) == "string" then
863 table.insert(self.deps, {deps=deps, add=""})
866 -- Generates the unique CBID
867 function AbstractValue.cbid(self, section)
868 return "cbid."..self.map.config.."."..section.."."..self.option
871 -- Return whether this object should be created
872 function AbstractValue.formcreated(self, section)
873 local key = "cbi.opt."..self.config.."."..section
874 return (luci.http.formvalue(key) == self.option)
877 -- Returns the formvalue for this object
878 function AbstractValue.formvalue(self, section)
879 return luci.http.formvalue(self:cbid(section))
882 function AbstractValue.additional(self, value)
883 self.optional = value
886 function AbstractValue.mandatory(self, value)
887 self.rmempty = not value
890 function AbstractValue.parse(self, section)
891 local fvalue = self:formvalue(section)
892 local cvalue = self:cfgvalue(section)
894 if fvalue and fvalue ~= "" then -- If we have a form value, write it to UCI
895 fvalue = self:transform(self:validate(fvalue, section))
897 self.tag_invalid[section] = true
899 if fvalue and not (fvalue == cvalue) then
900 self:write(section, fvalue)
902 else -- Unset the UCI or error
903 if self.rmempty or self.optional then
905 elseif self.track_missing and (not fvalue or fvalue ~= cvalue) then
906 self.tag_missing[section] = true
911 -- Render if this value exists or if it is mandatory
912 function AbstractValue.render(self, s, scope)
913 if not self.optional or self:cfgvalue(s) or self:formcreated(s) then
916 scope.cbid = self:cbid(s)
917 scope.striptags = luci.util.striptags
919 scope.ifattr = function(cond,key,val)
921 return string.format(
922 ' %s="%s"', tostring(key),
923 luci.util.pcdata(tostring( val
925 or (type(self[key]) ~= "function" and self[key])
933 scope.attr = function(...)
934 return scope.ifattr( true, ... )
937 Node.render(self, scope)
941 -- Return the UCI value of this object
942 function AbstractValue.cfgvalue(self, section)
943 local value = self.map:get(section, self.option)
944 if not self.cast or self.cast == type(value) then
946 elseif self.cast == "string" then
947 if type(value) == "table" then
950 elseif self.cast == "table" then
955 -- Validate the form value
956 function AbstractValue.validate(self, value)
960 AbstractValue.transform = AbstractValue.validate
964 function AbstractValue.write(self, section, value)
965 return self.map:set(section, self.option, value)
969 function AbstractValue.remove(self, section)
970 return self.map:del(section, self.option)
977 Value - A one-line value
978 maxlength: The maximum length
980 Value = class(AbstractValue)
982 function Value.__init__(self, ...)
983 AbstractValue.__init__(self, ...)
984 self.template = "cbi/value"
989 function Value.value(self, key, val)
991 table.insert(self.keylist, tostring(key))
992 table.insert(self.vallist, tostring(val))
996 -- DummyValue - This does nothing except being there
997 DummyValue = class(AbstractValue)
999 function DummyValue.__init__(self, ...)
1000 AbstractValue.__init__(self, ...)
1001 self.template = "cbi/dvalue"
1005 function DummyValue.parse(self)
1011 Flag - A flag being enabled or disabled
1013 Flag = class(AbstractValue)
1015 function Flag.__init__(self, ...)
1016 AbstractValue.__init__(self, ...)
1017 self.template = "cbi/fvalue"
1023 -- A flag can only have two states: set or unset
1024 function Flag.parse(self, section)
1025 local fvalue = self:formvalue(section)
1028 fvalue = self.enabled
1030 fvalue = self.disabled
1033 if fvalue == self.enabled or (not self.optional and not self.rmempty) then
1034 if not(fvalue == self:cfgvalue(section)) then
1035 self:write(section, fvalue)
1038 self:remove(section)
1045 ListValue - A one-line value predefined in a list
1046 widget: The widget that will be used (select, radio)
1048 ListValue = class(AbstractValue)
1050 function ListValue.__init__(self, ...)
1051 AbstractValue.__init__(self, ...)
1052 self.template = "cbi/lvalue"
1057 self.widget = "select"
1059 if not self.override_scheme
1060 and self.map:get_scheme(self.section.sectiontype, self.option) then
1061 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1062 if self.value and vs.values and not self.override_values then
1063 if self.rmempty or self.optional then
1066 for k, v in pairs(vs.values) do
1068 if not self.override_dependencies
1069 and vs.enum_depends and vs.enum_depends[k] then
1070 for i, dep in ipairs(vs.enum_depends[k]) do
1071 table.insert(deps, _uvl_strip_remote_dependencies(dep))
1074 self:value(k, v, unpack(deps))
1080 function ListValue.value(self, key, val, ...)
1081 if luci.util.contains(self.keylist, key) then
1086 table.insert(self.keylist, tostring(key))
1087 table.insert(self.vallist, tostring(val))
1089 for i, deps in ipairs({...}) do
1090 table.insert(self.deps, {add = "-"..key, deps=deps})
1094 function ListValue.validate(self, val)
1095 if luci.util.contains(self.keylist, val) then
1105 MultiValue - Multiple delimited values
1106 widget: The widget that will be used (select, checkbox)
1107 delimiter: The delimiter that will separate the values (default: " ")
1109 MultiValue = class(AbstractValue)
1111 function MultiValue.__init__(self, ...)
1112 AbstractValue.__init__(self, ...)
1113 self.template = "cbi/mvalue"
1118 self.widget = "checkbox"
1119 self.delimiter = " "
1122 function MultiValue.render(self, ...)
1123 if self.widget == "select" and not self.size then
1124 self.size = #self.vallist
1127 AbstractValue.render(self, ...)
1130 function MultiValue.value(self, key, val)
1131 if luci.util.contains(self.keylist, key) then
1136 table.insert(self.keylist, tostring(key))
1137 table.insert(self.vallist, tostring(val))
1140 function MultiValue.valuelist(self, section)
1141 local val = self:cfgvalue(section)
1143 if not(type(val) == "string") then
1147 return luci.util.split(val, self.delimiter)
1150 function MultiValue.validate(self, val)
1151 val = (type(val) == "table") and val or {val}
1155 for i, value in ipairs(val) do
1156 if luci.util.contains(self.keylist, value) then
1157 result = result and (result .. self.delimiter .. value) or value
1165 StaticList = class(MultiValue)
1167 function StaticList.__init__(self, ...)
1168 MultiValue.__init__(self, ...)
1170 self.valuelist = self.cfgvalue
1172 if not self.override_scheme
1173 and self.map:get_scheme(self.section.sectiontype, self.option) then
1174 local vs = self.map:get_scheme(self.section.sectiontype, self.option)
1175 if self.value and vs.values and not self.override_values then
1176 for k, v in pairs(vs.values) do
1183 function StaticList.validate(self, value)
1184 value = (type(value) == "table") and value or {value}
1187 for i, v in ipairs(value) do
1188 if luci.util.contains(self.valuelist, v) then
1189 table.insert(valid, v)
1196 DynamicList = class(AbstractValue)
1198 function DynamicList.__init__(self, ...)
1199 AbstractValue.__init__(self, ...)
1200 self.template = "cbi/dynlist"
1206 function DynamicList.value(self, key, val)
1208 table.insert(self.keylist, tostring(key))
1209 table.insert(self.vallist, tostring(val))
1212 function DynamicList.validate(self, value, section)
1213 value = (type(value) == "table") and value or {value}
1216 for i, v in ipairs(value) do
1218 not luci.http.formvalue("cbi.rle."..section.."."..self.option.."."..i) then
1219 table.insert(valid, v)
1228 TextValue - A multi-line value
1231 TextValue = class(AbstractValue)
1233 function TextValue.__init__(self, ...)
1234 AbstractValue.__init__(self, ...)
1235 self.template = "cbi/tvalue"
1241 Button = class(AbstractValue)
1243 function Button.__init__(self, ...)
1244 AbstractValue.__init__(self, ...)
1245 self.template = "cbi/button"
1246 self.inputstyle = nil