luci之how to write modules

本文档默认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





你可能感兴趣的:(luci之how to write modules)