OpenWrt的Luci之Web页面开发

翻译自github:https://github.com/openwrt/luci/wiki/ModulesHowTo

或官方http://luci.subsignal.org/trac/wiki/Documentation/ModulesHowTo

编写LUCI模块

1、Luci简介

LuCI是OpenWrt上的Web管理界面,LuCI采用了MVC三层架构,使用Lua脚本开发,所以开发LuCI的配置界面不需要编辑任何的Html代码,除非想自己单独去创建网页(View层),否则我们基本上只需要修改Model层就可以了

lucidir:/usr/lib/lua/luci

LuCI安装可通过您的网络服务器通过/cgi-bin/luci访问

2、调度过程(The dispatching process)

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(路径, 调用目标, _("显示名称"), 显示顺序)
  • path 是一个描述调度树中位置的表:例如,{"foo","bar","baz"}的路径会将您的节点插入foo.bar.baz。
  • target 描述用户请求节点时将采取的操作。有几个预定义的,其中3个最重要的(call, template, cbi)将在本页后面描述
  • title 定义菜单中用户可见的标题(可选)
  • order 是一个数字,同一级别的节点将在菜单中排序(可选)

您可以通过操作entry-function返回的节点表来分配更多属性。一些示例属性:

  • i18n 定义了在请求页面时应自动加载哪个翻译文件
  • dependent 保护插件在父节点丢失时从上下文中调用
  • leaf 停止在此节点处解析请求,并且在调度树中不再进一步
  • sysauth 要求用户使用给定的系统用户帐户进行身份验证

 

3、命名和模块文件

既然您已了解有关调度的基础知识,我们就可以开始编写模块了。但在您必须选择新数字孩子的类别和名称之前。

我们假设您要创建一个带有模块“mymodule”的新应用程序“myapp”。

因此,您必须使用文件mymodule.lua创建一个新的子目录lucidir/controller/myapp,其中包含以下内容:

module("luci.controller.myapp.mymodule", package.seeall)

function index()

end

Lua需要第一行才能正确识别模块并创建其范围。index-Function将用于在调度树中注册操作。

 

4、开始

使用新建模块

Actions

重新打开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中的重定向功能。

Views

如果只想向用户显示文本或一些有趣的熟悉照片,使用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模板处理器使用。最好在页面和页脚的开头和结尾包含页眉和页脚,因为它们会创建默认设计和菜单。

 

5、CBI模型

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附带的模块。

 

你可能感兴趣的:(Luci,Openwrt,Lua)