本文档默认luci的安装路径是:/usr/lib/lua/luci ,而且要确保我们安装的LUCI可以使用web服务器访问到,路径是/cgi-bin/luci
一、分发过程
1、为了编写一个luci模块,你必须清除luci的基本分发过程。Luci使用dispatching tree,这个dispatching tree会在执行每一个有效的controller的index函数的时候被建立,
CGI-environment的变量PATH_INFO会被用作dispatching tree的路径变量,比如:/cgi-bin/luci/foo/ba/baz路径会被解析成foo.bar.baz。
2、使用luci.dispatcher.entry函数来注册一个dispatching tree。entry函数带有4个参数,其中后面两个是可选的:
entry(path, target, title=nil, order=nil)
patch, 表示这个dispatching tree所在的位置,比如路径 {"foo", "bar", "baz"},就表示将会把这个节点插入在:foo.bar.baz
target,表示当用户请求这个node的时候,这个动作将被执行,其中最重要的三个target(call,template,cbi)将会在后面
的文章中举例说明。
title:定义了这个菜单显示的标题名称,可选。
order:将决定了同级别菜单中的排列顺序。
我们可以通过操作entry函数的返回值来定义更多的属性。可以操作的属性如下:
il8n:定义了在页面被请求的时候,那些翻译的文件将会被加载。
dependent:当节点的父节点不错在的时候,可以然这个节点不显示。
leaf:停止解析对这个节点的请求,不再分发这个dispatching tree
sysauth:要求用户使用系统给定的帐号认证
二、命名和模块文件。
至此,我们知道了dispatching tree的分发过程,接下来可以开始写模块了。但是在此之前,我们还需要选择这个新的digital child
的种类和名称。我们假定要建立一个新的application: “myapp” ,模块名称为“mymodule”。
所以,我们必须建立一个新的子目录:lucidir/controller/myapp和该目录下的lua文件mymodule.lua,文件内容如下:
module("luci.controller.myapp.mymodule", package.seeall)
function index()
end
第一行是声明一个
模块,并且建立他的作用域,index函数将用于在dispatching tree中注册actions。
三、指明我们的新的子菜单动作
目前,我们新建的菜单有一个名字了,但是还没有相应的动作。我们假设我们会在最后一步重新使用我们的模块myapp.mymodule
1、重新打开lucidir/controller/myapp/mymodule.lua,然后添加一个函数,代码如下例子:
module("luci.controller.myapp.mymodule", package.seeall) function index() entry({"click", "here", "now"}, call("action_tryme"), "Click here", 10).dependent=false end function action_tryme() luci.http.prepare_content("text/plain") luci.http.write("Haha, rebooting now...") luci.sys.reboot() end现在,在浏览器中键入: /cgi-bin/luci/click/here/now ( http://192.168.1.1/luci/click/here/now)
我们会发现这些动作函数例子必须添加到dispatching entry中。
另外我们需要知道,CGI的说明要求我们在发送正文content之前,发送一个content-type 头给CG。我们会看到一些快捷途径(就像上面的例子展示的那样),比如重定向函数luci.http.prepare_content(),
四、Views
如果你只想展示一些用户的文字或者有趣的照,那么使用HTML-template就足够了,这些template也会包含一些lua代码,但是请注意,如果你把整个套件都使用这些template那么有可能被其他开发者称为没用的“垃圾”。
现在,然我们建立一个标题template, 在文件/lucidir/view/myapp-mymodule/helloword.htm文件中编码如下:
<%+header%> <h1><%:Hello World%></h1> <%+footer%>然后,在controller中的index-function添加如下代码:
entry({"my", "new", "template"}, template("myapp-mymodule/helloworld"), "Hello world", 20).dependent=false现在,在浏览器中键入:
/cgi-bin/luci/my/new/template (http://192.168.1.1/luci/my/new/template
我们可以看到那些fancy-tags,这些template markups是提供给temlate处理程序使用的。我们在将默认的页面header和footer加载进来能产生不错的效果。
四、CBI models
CBI 是LUCI最强大、最酷的特性之一,他建立一种根据用户的交互界面,建立一种套路,并且将用户的配置信息保存到一个指明的UCI配置文件中去。我们只需要描述CBI模块中的这个配置文件的结构,然后luci就会帮我们把后续的工作全部做完,包括产生、解析、确认一个XHTML from还有读、写UCI配置文件。
现在,让我们来认真地研究一下下面这段代码,然后创建一个真正的实验例子:
m = Map("network", "Network") -- We want to edit the uci config file /etc/config/network s = m:section(TypedSection, "interface", "Interfaces") -- Especially the "interface"-sections s.addremove = true -- Allow the user to create and remove the interfaces function s:filter(value) return value ~= "loopback" and value -- Don't touch loopback end s:depends("proto", "static") -- Only show those with "static" s:depends("proto", "dhcp") -- or "dhcp" as protocol and leave PPPoE and PPTP alone p = s:option(ListValue, "proto", "Protocol") -- Creates an element list (select box) p:value("static", "static") -- Key and value pairs p:value("dhcp", "DHCP") p.default = "static" s:option(Value, "ifname", "interface", "the physical interface to be used") -- This will give a simple textbox s:option(Value, "ipaddr", translate("IP Address")) -- Ja, das ist eine i18n-Funktion ;-) s:option(Value, "netmask", "Netmask"):depends("proto", "static") -- You may remember this "depends" function from above mtu = s:option(Value, "mtu", "MTU") mtu.optional = true -- This one is very optional dns = s:option(Value, "dns", "DNS-Server") dns:depends("proto", "static") dns.optional = true function dns:validate(value) -- Now, that's nifty, eh? return value:match("[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+") -- Returns nil if it doesn't match otherwise returns match end gw = s:option(Value, "gateway", "Gateway") gw:depends("proto", "static") gw.rmempty = true -- Remove entry if it is empty return m -- Returns the map当然,不要忘记在controller的index-Function中添加以下代码:
entry({"admin", "network", "interfaces"}, cbi("myapp-mymodule/netifaces"), "Network interfaces", 30).dependent=false还有其他很多的LUCI特性,可以看:
https://github.com/openwrt/luci/wiki/CBI