翻译自github:https://github.com/openwrt/luci/wiki/ModulesHowTo
或官方http://luci.subsignal.org/trac/wiki/Documentation/ModulesHowTo
LuCI是OpenWrt上的Web管理界面,LuCI采用了MVC三层架构,使用Lua脚本开发,所以开发LuCI的配置界面不需要编辑任何的Html代码,除非想自己单独去创建网页(View层),否则我们基本上只需要修改Model层就可以了
lucidir:/usr/lib/lua/luci
LuCI安装可通过您的网络服务器通过/cgi-bin/luci访问
LuCI使用一个调度树,它将通过执行每个可用控制器的index-Function来构建。CGI环境变量PATH_INFO将用作此调度树中的路径,例如:/cgi-bin/luci/foo/bar/baz 将被解析为foo.bar.baz
要注册在调度树中的功能,您可以使用入口的-function luci.dispatcher,entry需要4个参数(2个是可选的):
entry(path, target, title=nil, order=nil)
//entry(路径, 调用目标, _("显示名称"), 显示顺序)
您可以通过操作entry-function返回的节点表来分配更多属性。一些示例属性:
既然您已了解有关调度的基础知识,我们就可以开始编写模块了。但在您必须选择新数字孩子的类别和名称之前。
我们假设您要创建一个带有模块“mymodule”的新应用程序“myapp”。
因此,您必须使用文件mymodule.lua创建一个新的子目录lucidir/controller/myapp,其中包含以下内容:
module("luci.controller.myapp.mymodule", package.seeall)
function index()
end
Lua需要第一行才能正确识别模块并创建其范围。index-Function将用于在调度树中注册操作。
使用新建模块
重新打开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://localhost:8080/luci/click/here/now,如果您正在使用开发环境)
您会看到这些简单的动作函数必须添加到调度条目中。
您可能知道或不知道:CGI规范要求您在发送内容之前发送Content-Type标头。您将找到几个快捷方式(如上面使用的快捷方式)以及模块luci.http中的重定向功能。
如果只想向用户显示文本或一些有趣的熟悉照片,使用html模板就足够了。这些模板还可以包含一些Lua代码,但是要注意,仅使用这些模板编写整个办公套件可能会被其他开发人员称为“脏”。
现在让我们创建一个小模板lucidir/view/myapp-mymodule/helloworld.htm,内容如下:
<%+header%>
<%:Hello World%>
<%+footer%>
并将以下行添加到模块文件的index-Function中。
entry({"my", "new", "template"}, template("myapp-mymodule/helloworld"), "Hello world", 20).dependent=false
现在在浏览器中输入/cgi-bin/luci/my/new/template (http://localhost:8080/luci/my/new/template,如果您正在使用开发环境)
您可能会注意到那些花哨的<%%> - 标签,这些是标记吗?由LuCI模板处理器使用。最好在页面和页脚的开头和结尾包含页眉和页脚,因为它们会创建默认设计和菜单。
CBI是LuCI最酷的功能之一。它创建一个基于公式的用户界面,并将其内容保存到特定的UCI配置文件中。您只需要在CBI模型文件中描述配置文件的结构,Luci将完成剩下的工作。这包括生成,解析和验证XHTML表单以及读取和写入UCI文件。
所以,至少对于这一段我们是认真的,并创建一个真实的例子,
lucidir/model/cbi/myapp-mymodule/netifaces.lua 内容如下:
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", "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
当然不要忘记在模块的index-Function中添加这样的东西。
entry({"admin", "network", "interfaces"}, cbi("myapp-mymodule/netifaces"), "Network interfaces", 30).dependent=false
还有更多功能,请参阅CBI参考和LuCI附带的模块。