LuCI是OpenWrt上的Web管理界面,LuCI采用了MVC三层架构。
MVC(Model-View-Controller)是软件工程中的一种软件架构模式,把软件系统分为三个基本部分:模型(Model)、视图(View)和控制器(Controller)。
/www/index.html
#引导页面
/cgi-bin/luci
#脚本跳转
/luci-static/*/*.css、*.js、*.gif
#静态页面css js gif 等目录
/usr/lib/lua/nixio.so、uci.so
#库文件,调用接口用
/usr/lib/lua/luci/
#mvc 层需要的脚本及库文件存放目录
/usr/lib/lua/luci/controller/*.lua
# lua 文件里,定义了菜单的显示和html 以及业务处理路径,每个文件对应一个菜单项。
# ./admin/index.lua 这个文件定义了node ,最外面的节点,最上层菜单的显示。
/usr/lib/lua/luci/model/*.lua
#对应controller 里面调用的脚本文件,下文会解析, 这里不再赘述
/usr/lib/lua/luci/view/*.lua
#对应controller 里面调用的静态页面文件
从MVC结构可见, LUCI工作是从CONTROLLER中的脚本开始运行, 我从luci的目录结构中选用常用的系统页面来举例说明。
module("luci.controller.admin.system", package.seeall)
function index()
local fs = require "nixio.fs"
entry({"admin", "system"}, alias("admin", "system", "system"), _("System"), 30).index = true
entry({"admin", "system", "system"}, cbi("admin_system/system"), _("System"), 1)
entry({"admin", "system", "clock_status"}, post_on({ set = true }, "action_clock_status"))
entry({"admin", "system", "admin"}, cbi("admin_system/admin"), _("Administration"), 2)
.......
end
entry({"admin", "system"}, alias("admin", "system", "system"), _("System"), 30).index = true
-- 定义system的最外层的菜单栏 -- alias是指向别的entry的别名
entry({"admin", "system", "system"}, cbi("admin_system/system"), _("System"), 1)
在luci里面entry的全定义entry(path, target, title=nil, order=nil)
path:对应的就是菜单路径 {“admin”, “system”}或者{“admin”, “system”, “system”}
target:调用目标分为三种,分别是执行指定方法Action、访问指定页面Views以及调用CBI Module
title:菜单栏显示名,_(“string”), 多语时调用这个来翻译对应语言
order: 同级菜单下,此菜单项的位置,从小到大,表现为从左到右,从上到下
2.1 当中提到过代码创建管理权菜单中,调用了cbi(“admin_system/admin”)
-- 菜单调用项
entry({"admin", "system", "admin"}, cbi("admin_system/admin"), _("Administration"), 2)
-- /usr/lib/lua/luci/model/cbi 目录下有 admin_system/admin.lua
对应实际页面中
CBI模型是Lua文件描述UCI配置文件的结构和由此产生的HTML表单来评估CBI解析器,所有CBI luci.cbi.Map类型的模型文件必须返回一个map对象,在cbi模块中定义各种控件,Luci系统会自动执行大部分处理工作。
m = Map("system", translate("Router Password"),translate("Changes the administrator password for accessing the device"))
-- 第一个参数,代表cbi 基于配置文件system建立Map, 这里的system 实际路径 /etc/config/system
-- 第二个参数, 映射页面的标题,第三个参数, 功能简介
-- 一个配置文件可以map多个文件页面
根据配置文件生成生成对应的section
s = m:section(TypedSection, "_dummy", "")
--表示获取所有类型为空的section并生成html
-- _dummy 这里差不多和空值等价,因为密码不需要填充值
s.addremove = false
-- 设定不允许增加或删除Section
s.anonymous = true
-- 设定不显示Section的名称
pw1 = s:option(Value, "pw1", translate("Password"))
-- 生成输入框
pw1.password = true
-- 设置输入框属性
pw2 = s:option(Value, "pw2", translate("Confirmation"))
pw2.password = true
section 函数根据配置
TypedSection表示根据类型获取section
NamedSection表示根据名字获取section
function s.cfgsections()
return { "_pass" }
end
-- 上面显示section名函数
function m.parse(map)
local v1 = pw1:formvalue("_pass")
local v2 = pw2:formvalue("_pass")
if v1 and v2 and #v1 > 0 and #v2 > 0 then
if v1 == v2 then
if luci.sys.user.setpasswd(luci.dispatcher.context.authuser, v1) == 0 then
--设置密码接口luci.sys.user.setpasswd, 查询接口见下面。
m.message = translate("Password successfully changed!")
else
m.message = translate("Unknown Error, password not changed!")
end
else
m.message = translate("Given password confirmation did not match, password not changed!")
end
end
Map.parse(map)
end
-- 判断设置密码函数
luci接口手册
nixio接口手册
[1] WIKI
[2] 部分前辈的博客