Executing a LuCI command from the Linux shellNote: -l Used to load the module -e Executes some lua script root@OpenWrt:~# lua
-lluci.sys -e 'print(luci.sys.sysinfo())' Config file for init-scripts and dependences/etc/config/unitrac k Disable Authentication on a web pagesetting sysauth=false for the node does the job, therefore no change is required.CBI: How to filter based in the value of an optionFor example: to be able to display interfaces based on the "proto" optionm = Map("network", "title", "description") s = m:section(TypedSection, "interface", "") function s.filter(self, section) return self.map:get(section, "proto") ~= "gre" <---- here is the magic (exclude "gre", because the web interface does not know how to display it and will mangle it ) end CBI: Validating fieldsIn you have a requirement to validate that all parameters are unique, or have other relationships that cannot be defined in the schema you will need to be able to validate ALL parameters on a page level rather than use the existing CBI option or section level validationTo Validate a field/optionIf you require a validation of field or option, you can define a validate function for for the option, the value passed is the uncommitted value from the submitted form. returning a value of 'nil' indicates that the value is in error, you may also pass back an error message. to be displayed. If you don't a default message will be show NOTE: boolean options don't call a validate function m = Map(...) s = m:section(...) o = option(...) function o.validate(self, value) if value < min then return nil, 'too small' end if value > max then return nil, 'too big' end return value -- just right end To Validate a sectionIf you require a validation of a section you can define a validate function for a whole section, this allows you to validate all fields within it. It gets self and the section id as parameters. The fields are stored in a key->value table within self.fields, you can access their :formvalue() member function to obtain the values you want. Try something like that: m = Map(...) s = m:section(...) function s.validate(self, sectionid) local field, obj local values = { } for field, obj in pairs(self.fields) do local fieldval = obj:formvalue(sectionid) if not values[fieldval] then values[fieldval] = true else return nil -- raise error end end return sectionid -- signal success end To Validate a whole pageWhen a CBI map is rendered after a form submit, the associated controller calls m:parse() which triggers form value fetching and validation across all sections within the map, each section in turn does validation on each fields within it. If one of the field validation functions fail, the whole map is flagged invalid by setting .save = false on the map object. When a validation error occurs on a per-field or per-section level, the associated error handling sets the property .error to a table value to flag the error condition in a specific field (or section) - this is used by the templates later on to highlight failed options (for example make them red and print an error text next to it). The error structure looks like this: .error = { [section_id_1] = "Error message string", [section_id_2] = "Another error message" } The format is the same for per-section and per-option errors. The variables section_id_1 and section_id_2 are the uci identifiers of the underlying section e.g. "lan" for "config interface lan" and "cfg12ab23f" for "config foo" (anonymous section). To complicate it further, sections of the type TypedSection are not 1:1 mapped to the CBI model, one TypeSection declaration may result in multiple actual section blocks rendered in the form so you need to treat it correctly when traversing the map->sections->options tree. With that in mind, you can hijack the Map's .parse function to implement some kind of global validation: local utl = require "luci.util" m = Map("config", ...) function m.parse(self, ...) -- NB: "..." actually means ellipsis here -- call the original parse implementation Map.parse(self, ...) -- do custom processing local sobj for _, sobj in ipairs(self.children) do local sids -- check section type if utl.instanceof(sobj, NamedSection) then -- this is a named section, -- the uci id of this section is -- stored in sobj.section sids = { sobj.section } elseif utl.instanceof(sobj, TypedSection) then -- this is a typed section, -- it may map to multiple uci ids sids = sobj:cfgsections() end -- now validate the fields within this section -- for each associated config section local sid, fld for _, sid in ipairs(sids) do
-- get the value for a specific field in -- a specific section local val = fld:formvalue(sid) -- do some custom checks on val, -- e.g. compare against :cfgvalue(), -- some global structure etc. if not is_valid(val, other_stuff) then -- failed, flag map (self == m) self.save = false -- create field error for -- template highlight fld.error = { [sid] = "Error foobar" } end end end end end Regenerate translation POT filesYou need to checkout the LuCI tree and use the "i18n-scan.pl" script in build/. Use that to scan the subdir containing the code and it willoutput the *.pot code to stdout: $ ./build/i18n-scan.pl applications/luci-polipo/ msgid "enable" msgstr "" #. Polipo msgid "polipo" msgstr "Polipo" msgid "enable" msgstr "" #. Polipo msgid "polipo" msgstr "Polipo"
Use them like this: m = Map("foo", ...) m.on_after_commit = function(self) -- all written config names are in self.parsechain local file for _, file in ipairs(self.parsechain) do -- copy "file" ... done end Schemaoption typeone of { "enum", "lazylist", "list", "reference", "variable" } option datatypeone of {"Integer", "Boolean", "String"} Getting Anonymous UCI Config DataWhen accessing "anonymous" sections via LUA do the following: local hostname luci.model.uci.cursor():foreach("system", "system", function(s) hostname = s.hostname end) print(hostname) Since this is often needed, we added a shortcut doing exactly that: local hostname = luci.model.uci.cursor():get_first("system", "system", "hostname") print(hostname) See also http://luci.subsignal.org/api/luci/modules/luci.model.uci.html#Cursor.get_first Enabling / Disabling Authentication enable authentication you need to set the sysauth properties on your root-level node:x = entry({"myApp"}, template("myApp/overview"), "myApp", 1) x.dependant = false x.sysauth = "root" x.sysauth_authenticator = "htmlauth" (see controller/admin/index.lua) To make your site the index, use: local root = node() root.target = alias("myApp") root.index = true This should work as long as the name of your app > "admin" due to alphabetical sorting. Using a template to create custom fieldsCreate a new view e.g. luasrc/view/cbi_timeval.htm like this Important are the includes at the beggining and the end, and that the id, name and value attributes are correct. The rest can be adapted. In your Model do something like this.
Save vs Save & Apply[Save] pushes the change to /etc/config /* and [Save & Apply] does the same plus it calls corresponding init scripts defined in /etc/config/ucitrack . If my custom page only needs to write to /etc/config/myapp.lua but not reboot the router, how do I get ONLY a [Save] button?Change the cbi() invocation in your controller to something like this: cbi("my/form", {autoapply=true}) Run a Script from a ButtonThis bit of code needs "s" to be a section from either a SimpleForm or a Map btn = s:option(Button, "_btn", translate("Click this to run a script")) function btn.write() luci.sys.call("/usr/bin/script.sh") end Accessing multiple config filesm = Map.('config', 'label', 'description') m.chain('2nd-config') -- do stuff with m m1 = Map('2nd-config', 'label', 'description') -- do stuff with m1 return m, m1 -- need to return both maps |
OpenWrt >