2008-03-02 21:52:58 +00:00
|
|
|
--[[
|
2008-05-25 17:00:30 +00:00
|
|
|
LuCI - Dispatcher
|
2008-03-02 21:52:58 +00:00
|
|
|
|
|
|
|
Description:
|
|
|
|
The request dispatcher and module dispatcher generators
|
|
|
|
|
|
|
|
FileId:
|
|
|
|
$Id$
|
|
|
|
|
|
|
|
License:
|
|
|
|
Copyright 2008 Steven Barth <steven@midlink.org>
|
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
you may not use this file except in compliance with the License.
|
2008-05-24 22:58:45 +00:00
|
|
|
You may obtain a copy of the License at
|
2008-03-02 21:52:58 +00:00
|
|
|
|
2008-05-24 22:58:45 +00:00
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
2008-03-02 21:52:58 +00:00
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
limitations under the License.
|
|
|
|
|
|
|
|
]]--
|
2008-07-29 20:32:02 +00:00
|
|
|
|
|
|
|
--- LuCI web dispatcher.
|
2009-07-19 00:24:58 +00:00
|
|
|
local fs = require "nixio.fs"
|
2008-08-29 23:26:01 +00:00
|
|
|
local sys = require "luci.sys"
|
|
|
|
local init = require "luci.init"
|
|
|
|
local util = require "luci.util"
|
|
|
|
local http = require "luci.http"
|
2009-06-21 13:42:26 +00:00
|
|
|
local nixio = require "nixio", require "nixio.util"
|
2008-03-02 21:52:58 +00:00
|
|
|
|
2008-08-29 23:26:01 +00:00
|
|
|
module("luci.dispatcher", package.seeall)
|
2008-11-29 21:58:39 +00:00
|
|
|
context = util.threadlocal()
|
2009-11-10 16:02:48 +00:00
|
|
|
uci = require "luci.model.uci"
|
2009-11-29 13:46:04 +00:00
|
|
|
i18n = require "luci.i18n"
|
2009-11-14 18:41:16 +00:00
|
|
|
_M.fs = fs
|
2008-03-29 18:22:21 +00:00
|
|
|
|
2008-08-10 12:58:05 +00:00
|
|
|
authenticator = {}
|
|
|
|
|
2008-05-26 12:16:16 +00:00
|
|
|
-- Index table
|
2008-06-14 14:12:12 +00:00
|
|
|
local index = nil
|
2008-05-26 12:16:16 +00:00
|
|
|
|
2008-06-02 15:36:13 +00:00
|
|
|
-- Fastindex
|
|
|
|
local fi
|
|
|
|
|
2008-05-05 19:27:30 +00:00
|
|
|
|
2008-07-29 20:32:02 +00:00
|
|
|
--- Build the URL relative to the server webroot from given virtual path.
|
|
|
|
-- @param ... Virtual path
|
|
|
|
-- @return Relative URL
|
2008-05-29 13:51:32 +00:00
|
|
|
function build_url(...)
|
2008-12-15 10:40:45 +00:00
|
|
|
local path = {...}
|
2010-11-13 13:50:54 +00:00
|
|
|
local url = { http.getenv("SCRIPT_NAME") or "" }
|
|
|
|
|
|
|
|
local k, v
|
2008-12-15 10:40:45 +00:00
|
|
|
for k, v in pairs(context.urltoken) do
|
2010-11-13 13:50:54 +00:00
|
|
|
url[#url+1] = "/;"
|
|
|
|
url[#url+1] = http.urlencode(k)
|
|
|
|
url[#url+1] = "="
|
|
|
|
url[#url+1] = http.urlencode(v)
|
2008-12-15 10:40:45 +00:00
|
|
|
end
|
2010-11-13 13:50:54 +00:00
|
|
|
|
|
|
|
local p
|
|
|
|
for _, p in ipairs(path) do
|
2010-11-13 20:50:20 +00:00
|
|
|
if p:match("^[a-zA-Z0-9_%-%.%%/,;]+$") then
|
2010-11-13 13:50:54 +00:00
|
|
|
url[#url+1] = "/"
|
|
|
|
url[#url+1] = p
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
return table.concat(url, "")
|
2008-03-02 21:52:58 +00:00
|
|
|
end
|
|
|
|
|
2008-07-29 20:32:02 +00:00
|
|
|
--- Send a 404 error code and render the "error404" template if available.
|
|
|
|
-- @param message Custom error message (optional)
|
|
|
|
-- @return false
|
2008-03-02 21:52:58 +00:00
|
|
|
function error404(message)
|
2008-05-25 17:00:30 +00:00
|
|
|
luci.http.status(404, "Not Found")
|
2008-03-02 21:52:58 +00:00
|
|
|
message = message or "Not Found"
|
2008-05-24 22:58:45 +00:00
|
|
|
|
2008-05-25 17:00:30 +00:00
|
|
|
require("luci.template")
|
2008-06-14 14:12:12 +00:00
|
|
|
if not luci.util.copcall(luci.template.render, "error404") then
|
2008-05-25 17:00:30 +00:00
|
|
|
luci.http.prepare_content("text/plain")
|
2008-06-14 14:12:12 +00:00
|
|
|
luci.http.write(message)
|
2008-03-02 21:52:58 +00:00
|
|
|
end
|
2008-05-24 22:58:45 +00:00
|
|
|
return false
|
2008-03-02 21:52:58 +00:00
|
|
|
end
|
|
|
|
|
2008-07-29 20:32:02 +00:00
|
|
|
--- Send a 500 error code and render the "error500" template if available.
|
|
|
|
-- @param message Custom error message (optional)#
|
|
|
|
-- @return false
|
2008-03-02 21:52:58 +00:00
|
|
|
function error500(message)
|
2009-03-07 13:21:27 +00:00
|
|
|
luci.util.perror(message)
|
2009-02-09 13:17:26 +00:00
|
|
|
if not context.template_header_sent then
|
|
|
|
luci.http.status(500, "Internal Server Error")
|
2009-03-14 01:16:03 +00:00
|
|
|
luci.http.prepare_content("text/plain")
|
|
|
|
luci.http.write(message)
|
2009-02-09 13:17:26 +00:00
|
|
|
else
|
|
|
|
require("luci.template")
|
|
|
|
if not luci.util.copcall(luci.template.render, "error500", {message=message}) then
|
|
|
|
luci.http.prepare_content("text/plain")
|
|
|
|
luci.http.write(message)
|
|
|
|
end
|
2008-03-02 21:52:58 +00:00
|
|
|
end
|
2008-05-24 22:58:45 +00:00
|
|
|
return false
|
2008-03-02 21:52:58 +00:00
|
|
|
end
|
|
|
|
|
2008-08-22 22:13:54 +00:00
|
|
|
function authenticator.htmlauth(validator, accs, default)
|
2008-06-28 16:03:54 +00:00
|
|
|
local user = luci.http.formvalue("username")
|
|
|
|
local pass = luci.http.formvalue("password")
|
2008-09-23 00:10:51 +00:00
|
|
|
|
2008-08-10 12:58:05 +00:00
|
|
|
if user and validator(user, pass) then
|
|
|
|
return user
|
2008-06-28 16:03:54 +00:00
|
|
|
end
|
2008-09-23 00:10:51 +00:00
|
|
|
|
2008-08-10 12:58:05 +00:00
|
|
|
require("luci.i18n")
|
|
|
|
require("luci.template")
|
|
|
|
context.path = {}
|
|
|
|
luci.template.render("sysauth", {duser=default, fuser=user})
|
|
|
|
return false
|
2008-09-23 00:10:51 +00:00
|
|
|
|
2008-06-28 16:03:54 +00:00
|
|
|
end
|
|
|
|
|
2008-07-29 20:32:02 +00:00
|
|
|
--- Dispatch an HTTP request.
|
|
|
|
-- @param request LuCI HTTP Request object
|
2009-07-25 12:45:38 +00:00
|
|
|
function httpdispatch(request, prefix)
|
2008-06-14 14:12:12 +00:00
|
|
|
luci.http.context.request = request
|
2009-07-25 12:45:38 +00:00
|
|
|
|
|
|
|
local r = {}
|
|
|
|
context.request = r
|
[PATCH] Allow smarter node creation based on visibility during createtree
As I've brought up on the mailing list thread "High latency caused by full tree creation", there is a large amount of delay per LuCI request which is spent building the node tree in createtree(). Most nodes created aren't
needed for the view presented to the user and only serve to consume memory and CPU time during a page load.
My idea is to provide an easy mechanism for index()ers to determine which needs to be created and what isn't. Due to the constraints of the standard LuCI web interface, this optimization needs to establish a few rules:
* The page requested must have its node created
* All parents of the page being requested must be created, so the children inherit the track
* All the top-level nodes "Status", "System", "Services", "Network" (and others added by extensions) must be created in order to have their top-level tabs in the UI
* All peers of second-level nodes need to be created as well for the same reason, to display their links on the subindexes
To make this easy to implement in each controller, the attached patch adds an "inreq" field to each created node to indicate if it lies on the request path. To satisfy the "top level node" requirement, we always
add the top level node, then check its inreq property if the top-level node is not "in request", then the controller can exit index() early.
2011-08-12 11:05:59 +00:00
|
|
|
context.urltoken = {}
|
|
|
|
|
2009-01-20 19:40:14 +00:00
|
|
|
local pathinfo = http.urldecode(request:getenv("PATH_INFO") or "", true)
|
2008-05-24 22:58:45 +00:00
|
|
|
|
2009-07-25 12:45:38 +00:00
|
|
|
if prefix then
|
|
|
|
for _, node in ipairs(prefix) do
|
|
|
|
r[#r+1] = node
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
[PATCH] Allow smarter node creation based on visibility during createtree
As I've brought up on the mailing list thread "High latency caused by full tree creation", there is a large amount of delay per LuCI request which is spent building the node tree in createtree(). Most nodes created aren't
needed for the view presented to the user and only serve to consume memory and CPU time during a page load.
My idea is to provide an easy mechanism for index()ers to determine which needs to be created and what isn't. Due to the constraints of the standard LuCI web interface, this optimization needs to establish a few rules:
* The page requested must have its node created
* All parents of the page being requested must be created, so the children inherit the track
* All the top-level nodes "Status", "System", "Services", "Network" (and others added by extensions) must be created in order to have their top-level tabs in the UI
* All peers of second-level nodes need to be created as well for the same reason, to display their links on the subindexes
To make this easy to implement in each controller, the attached patch adds an "inreq" field to each created node to indicate if it lies on the request path. To satisfy the "top level node" requirement, we always
add the top level node, then check its inreq property if the top-level node is not "in request", then the controller can exit index() early.
2011-08-12 11:05:59 +00:00
|
|
|
local tokensok = true
|
2008-06-06 15:50:21 +00:00
|
|
|
for node in pathinfo:gmatch("[^/]+") do
|
[PATCH] Allow smarter node creation based on visibility during createtree
As I've brought up on the mailing list thread "High latency caused by full tree creation", there is a large amount of delay per LuCI request which is spent building the node tree in createtree(). Most nodes created aren't
needed for the view presented to the user and only serve to consume memory and CPU time during a page load.
My idea is to provide an easy mechanism for index()ers to determine which needs to be created and what isn't. Due to the constraints of the standard LuCI web interface, this optimization needs to establish a few rules:
* The page requested must have its node created
* All parents of the page being requested must be created, so the children inherit the track
* All the top-level nodes "Status", "System", "Services", "Network" (and others added by extensions) must be created in order to have their top-level tabs in the UI
* All peers of second-level nodes need to be created as well for the same reason, to display their links on the subindexes
To make this easy to implement in each controller, the attached patch adds an "inreq" field to each created node to indicate if it lies on the request path. To satisfy the "top level node" requirement, we always
add the top level node, then check its inreq property if the top-level node is not "in request", then the controller can exit index() early.
2011-08-12 11:05:59 +00:00
|
|
|
local tkey, tval
|
|
|
|
if tokensok then
|
|
|
|
tkey, tval = node:match(";(%w+)=([a-fA-F0-9]*)")
|
|
|
|
end
|
|
|
|
if tkey then
|
|
|
|
context.urltoken[tkey] = tval
|
|
|
|
else
|
|
|
|
tokensok = false
|
|
|
|
r[#r+1] = node
|
|
|
|
end
|
2008-03-02 21:52:58 +00:00
|
|
|
end
|
2008-05-24 22:58:45 +00:00
|
|
|
|
2009-02-26 16:45:01 +00:00
|
|
|
local stat, err = util.coxpcall(function()
|
2009-07-24 15:45:29 +00:00
|
|
|
dispatch(context.request)
|
2009-02-26 16:45:01 +00:00
|
|
|
end, error500)
|
2008-09-23 00:10:51 +00:00
|
|
|
|
2008-06-14 14:12:12 +00:00
|
|
|
luci.http.close()
|
2008-09-05 20:32:20 +00:00
|
|
|
|
|
|
|
--context._disable_memtrace()
|
2008-05-04 20:53:31 +00:00
|
|
|
end
|
|
|
|
|
2008-07-29 20:32:02 +00:00
|
|
|
--- Dispatches a LuCI virtual path.
|
|
|
|
-- @param request Virtual path
|
2009-07-24 15:45:29 +00:00
|
|
|
function dispatch(request)
|
2009-07-23 11:32:22 +00:00
|
|
|
--context._disable_memtrace = require "luci.debug".trap_memtrace("l")
|
2008-08-29 23:26:01 +00:00
|
|
|
local ctx = context
|
|
|
|
ctx.path = request
|
2008-09-23 00:10:51 +00:00
|
|
|
|
2009-01-14 23:47:56 +00:00
|
|
|
local conf = require "luci.config"
|
2009-03-14 01:16:03 +00:00
|
|
|
assert(conf.main,
|
|
|
|
"/etc/config/luci seems to be corrupt, unable to find section 'main'")
|
|
|
|
|
2010-04-16 19:05:05 +00:00
|
|
|
local lang = conf.main.lang or "auto"
|
2009-01-14 23:47:56 +00:00
|
|
|
if lang == "auto" then
|
|
|
|
local aclang = http.getenv("HTTP_ACCEPT_LANGUAGE") or ""
|
2009-01-30 15:29:14 +00:00
|
|
|
for lpat in aclang:gmatch("[%w-]+") do
|
|
|
|
lpat = lpat and lpat:gsub("-", "_")
|
2009-01-14 23:47:56 +00:00
|
|
|
if conf.languages[lpat] then
|
|
|
|
lang = lpat
|
|
|
|
break
|
|
|
|
end
|
|
|
|
end
|
2010-04-16 19:05:05 +00:00
|
|
|
end
|
2009-01-14 23:47:56 +00:00
|
|
|
require "luci.i18n".setlanguage(lang)
|
2008-09-23 00:10:51 +00:00
|
|
|
|
2009-07-23 03:25:27 +00:00
|
|
|
local c = ctx.tree
|
|
|
|
local stat
|
2009-07-24 15:45:29 +00:00
|
|
|
if not c then
|
|
|
|
c = createtree()
|
|
|
|
end
|
|
|
|
|
2008-05-22 14:04:03 +00:00
|
|
|
local track = {}
|
2008-08-07 19:03:25 +00:00
|
|
|
local args = {}
|
2008-11-01 18:49:41 +00:00
|
|
|
ctx.args = args
|
|
|
|
ctx.requestargs = ctx.requestargs or args
|
2008-08-07 19:03:25 +00:00
|
|
|
local n
|
2008-12-14 21:43:10 +00:00
|
|
|
local token = ctx.urltoken
|
|
|
|
local preq = {}
|
2009-01-04 15:45:57 +00:00
|
|
|
local freq = {}
|
2008-05-24 22:58:45 +00:00
|
|
|
|
2008-05-22 14:04:03 +00:00
|
|
|
for i, s in ipairs(request) do
|
[PATCH] Allow smarter node creation based on visibility during createtree
As I've brought up on the mailing list thread "High latency caused by full tree creation", there is a large amount of delay per LuCI request which is spent building the node tree in createtree(). Most nodes created aren't
needed for the view presented to the user and only serve to consume memory and CPU time during a page load.
My idea is to provide an easy mechanism for index()ers to determine which needs to be created and what isn't. Due to the constraints of the standard LuCI web interface, this optimization needs to establish a few rules:
* The page requested must have its node created
* All parents of the page being requested must be created, so the children inherit the track
* All the top-level nodes "Status", "System", "Services", "Network" (and others added by extensions) must be created in order to have their top-level tabs in the UI
* All peers of second-level nodes need to be created as well for the same reason, to display their links on the subindexes
To make this easy to implement in each controller, the attached patch adds an "inreq" field to each created node to indicate if it lies on the request path. To satisfy the "top level node" requirement, we always
add the top level node, then check its inreq property if the top-level node is not "in request", then the controller can exit index() early.
2011-08-12 11:05:59 +00:00
|
|
|
preq[#preq+1] = s
|
|
|
|
freq[#freq+1] = s
|
|
|
|
c = c.nodes[s]
|
|
|
|
n = i
|
|
|
|
if not c then
|
|
|
|
break
|
2008-05-24 22:58:45 +00:00
|
|
|
end
|
|
|
|
|
[PATCH] Allow smarter node creation based on visibility during createtree
As I've brought up on the mailing list thread "High latency caused by full tree creation", there is a large amount of delay per LuCI request which is spent building the node tree in createtree(). Most nodes created aren't
needed for the view presented to the user and only serve to consume memory and CPU time during a page load.
My idea is to provide an easy mechanism for index()ers to determine which needs to be created and what isn't. Due to the constraints of the standard LuCI web interface, this optimization needs to establish a few rules:
* The page requested must have its node created
* All parents of the page being requested must be created, so the children inherit the track
* All the top-level nodes "Status", "System", "Services", "Network" (and others added by extensions) must be created in order to have their top-level tabs in the UI
* All peers of second-level nodes need to be created as well for the same reason, to display their links on the subindexes
To make this easy to implement in each controller, the attached patch adds an "inreq" field to each created node to indicate if it lies on the request path. To satisfy the "top level node" requirement, we always
add the top level node, then check its inreq property if the top-level node is not "in request", then the controller can exit index() early.
2011-08-12 11:05:59 +00:00
|
|
|
util.update(track, c)
|
2008-09-23 00:10:51 +00:00
|
|
|
|
[PATCH] Allow smarter node creation based on visibility during createtree
As I've brought up on the mailing list thread "High latency caused by full tree creation", there is a large amount of delay per LuCI request which is spent building the node tree in createtree(). Most nodes created aren't
needed for the view presented to the user and only serve to consume memory and CPU time during a page load.
My idea is to provide an easy mechanism for index()ers to determine which needs to be created and what isn't. Due to the constraints of the standard LuCI web interface, this optimization needs to establish a few rules:
* The page requested must have its node created
* All parents of the page being requested must be created, so the children inherit the track
* All the top-level nodes "Status", "System", "Services", "Network" (and others added by extensions) must be created in order to have their top-level tabs in the UI
* All peers of second-level nodes need to be created as well for the same reason, to display their links on the subindexes
To make this easy to implement in each controller, the attached patch adds an "inreq" field to each created node to indicate if it lies on the request path. To satisfy the "top level node" requirement, we always
add the top level node, then check its inreq property if the top-level node is not "in request", then the controller can exit index() early.
2011-08-12 11:05:59 +00:00
|
|
|
if c.leaf then
|
|
|
|
break
|
2008-08-22 21:52:36 +00:00
|
|
|
end
|
2008-05-04 20:53:31 +00:00
|
|
|
end
|
2008-05-24 22:58:45 +00:00
|
|
|
|
2008-08-07 19:03:25 +00:00
|
|
|
if c and c.leaf then
|
|
|
|
for j=n+1, #request do
|
2009-01-04 15:45:57 +00:00
|
|
|
args[#args+1] = request[j]
|
|
|
|
freq[#freq+1] = request[j]
|
2008-08-07 19:03:25 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2009-09-11 10:46:06 +00:00
|
|
|
ctx.requestpath = ctx.requestpath or freq
|
2008-12-14 21:43:10 +00:00
|
|
|
ctx.path = preq
|
|
|
|
|
2008-05-22 14:04:03 +00:00
|
|
|
if track.i18n then
|
2011-08-12 12:34:13 +00:00
|
|
|
i18n.loadc(track.i18n)
|
2008-05-04 20:53:31 +00:00
|
|
|
end
|
2008-09-23 00:10:51 +00:00
|
|
|
|
2008-05-26 09:45:12 +00:00
|
|
|
-- Init template engine
|
2008-10-10 14:37:53 +00:00
|
|
|
if (c and c.index) or not track.notemplate then
|
2008-08-29 23:26:01 +00:00
|
|
|
local tpl = require("luci.template")
|
2008-10-19 20:49:10 +00:00
|
|
|
local media = track.mediaurlbase or luci.config.main.mediaurlbase
|
2009-08-07 15:16:14 +00:00
|
|
|
if not pcall(tpl.Template, "themes/%s/header" % fs.basename(media)) then
|
2008-09-29 15:38:13 +00:00
|
|
|
media = nil
|
|
|
|
for name, theme in pairs(luci.config.themes) do
|
|
|
|
if name:sub(1,1) ~= "." and pcall(tpl.Template,
|
|
|
|
"themes/%s/header" % fs.basename(theme)) then
|
|
|
|
media = theme
|
|
|
|
end
|
|
|
|
end
|
|
|
|
assert(media, "No valid theme found")
|
|
|
|
end
|
|
|
|
|
2009-07-23 03:25:27 +00:00
|
|
|
tpl.context.viewns = setmetatable({
|
2009-07-24 15:45:29 +00:00
|
|
|
write = luci.http.write;
|
|
|
|
include = function(name) tpl.Template(name):render(getfenv(2)) end;
|
2011-08-12 11:13:39 +00:00
|
|
|
translate = i18n.translate;
|
2010-10-15 16:12:07 +00:00
|
|
|
export = function(k, v) if tpl.context.viewns[k] == nil then tpl.context.viewns[k] = v end end;
|
2009-07-24 15:45:29 +00:00
|
|
|
striptags = util.striptags;
|
2009-10-31 15:42:07 +00:00
|
|
|
pcdata = util.pcdata;
|
2009-07-24 15:45:29 +00:00
|
|
|
media = media;
|
|
|
|
theme = fs.basename(media);
|
|
|
|
resource = luci.config.main.resourcebase
|
2009-07-23 03:25:27 +00:00
|
|
|
}, {__index=function(table, key)
|
2008-12-14 21:43:10 +00:00
|
|
|
if key == "controller" then
|
2008-12-15 10:40:45 +00:00
|
|
|
return build_url()
|
2008-12-14 21:43:10 +00:00
|
|
|
elseif key == "REQUEST_URI" then
|
2009-01-04 15:45:57 +00:00
|
|
|
return build_url(unpack(ctx.requestpath))
|
2008-12-14 21:43:10 +00:00
|
|
|
else
|
|
|
|
return rawget(table, key) or _G[key]
|
|
|
|
end
|
|
|
|
end})
|
2008-07-17 16:02:29 +00:00
|
|
|
end
|
2008-09-23 00:10:51 +00:00
|
|
|
|
2008-09-05 14:34:59 +00:00
|
|
|
track.dependent = (track.dependent ~= false)
|
2011-07-20 23:57:32 +00:00
|
|
|
assert(not track.dependent or not track.auto,
|
|
|
|
"Access Violation\nThe page at '" .. table.concat(request, "/") .. "/' " ..
|
|
|
|
"has no parent node so the access to this location has been denied.\n" ..
|
|
|
|
"This is a software bug, please report this message at " ..
|
|
|
|
"http://luci.subsignal.org/trac/newticket"
|
|
|
|
)
|
2008-09-23 00:10:51 +00:00
|
|
|
|
2008-06-28 16:03:54 +00:00
|
|
|
if track.sysauth then
|
2008-08-29 23:26:01 +00:00
|
|
|
local sauth = require "luci.sauth"
|
2008-09-23 00:10:51 +00:00
|
|
|
|
2008-08-22 20:04:04 +00:00
|
|
|
local authen = type(track.sysauth_authenticator) == "function"
|
|
|
|
and track.sysauth_authenticator
|
|
|
|
or authenticator[track.sysauth_authenticator]
|
2008-09-23 00:10:51 +00:00
|
|
|
|
2008-06-28 16:03:54 +00:00
|
|
|
local def = (type(track.sysauth) == "string") and track.sysauth
|
|
|
|
local accs = def and {track.sysauth} or track.sysauth
|
2008-12-14 21:43:10 +00:00
|
|
|
local sess = ctx.authsession
|
2008-12-15 10:40:45 +00:00
|
|
|
local verifytoken = false
|
2008-12-14 21:43:10 +00:00
|
|
|
if not sess then
|
|
|
|
sess = luci.http.getcookie("sysauth")
|
2009-07-25 10:47:26 +00:00
|
|
|
sess = sess and sess:match("^[a-f0-9]*$")
|
2008-12-15 10:40:45 +00:00
|
|
|
verifytoken = true
|
2008-12-14 21:43:10 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
local sdat = sauth.read(sess)
|
|
|
|
local user
|
|
|
|
|
|
|
|
if sdat then
|
2009-06-20 07:14:36 +00:00
|
|
|
sdat = loadstring(sdat)
|
|
|
|
setfenv(sdat, {})
|
|
|
|
sdat = sdat()
|
2008-12-14 21:43:10 +00:00
|
|
|
if not verifytoken or ctx.urltoken.stok == sdat.token then
|
|
|
|
user = sdat.user
|
|
|
|
end
|
2009-07-25 10:47:26 +00:00
|
|
|
else
|
|
|
|
local eu = http.getenv("HTTP_AUTH_USER")
|
|
|
|
local ep = http.getenv("HTTP_AUTH_PASS")
|
|
|
|
if eu and ep and luci.sys.user.checkpasswd(eu, ep) then
|
|
|
|
authen = function() return eu end
|
|
|
|
end
|
2008-12-14 21:43:10 +00:00
|
|
|
end
|
2008-09-23 00:10:51 +00:00
|
|
|
|
2008-08-29 23:26:01 +00:00
|
|
|
if not util.contains(accs, user) then
|
2008-08-10 12:58:05 +00:00
|
|
|
if authen then
|
2008-12-15 10:40:45 +00:00
|
|
|
ctx.urltoken.stok = nil
|
2008-08-26 00:53:28 +00:00
|
|
|
local user, sess = authen(luci.sys.user.checkpasswd, accs, def)
|
2008-08-29 23:26:01 +00:00
|
|
|
if not user or not util.contains(accs, user) then
|
2008-08-10 12:58:05 +00:00
|
|
|
return
|
|
|
|
else
|
2008-08-26 00:53:28 +00:00
|
|
|
local sid = sess or luci.sys.uniqueid(16)
|
|
|
|
if not sess then
|
2008-12-14 21:43:10 +00:00
|
|
|
local token = luci.sys.uniqueid(16)
|
|
|
|
sauth.write(sid, util.get_bytecode({
|
|
|
|
user=user,
|
|
|
|
token=token,
|
|
|
|
secret=luci.sys.uniqueid(16)
|
|
|
|
}))
|
2008-12-15 10:40:45 +00:00
|
|
|
ctx.urltoken.stok = token
|
2008-08-26 00:53:28 +00:00
|
|
|
end
|
2008-12-15 10:40:45 +00:00
|
|
|
luci.http.header("Set-Cookie", "sysauth=" .. sid.."; path="..build_url())
|
2008-09-05 14:28:36 +00:00
|
|
|
ctx.authsession = sid
|
2009-09-11 10:46:06 +00:00
|
|
|
ctx.authuser = user
|
2008-08-10 12:58:05 +00:00
|
|
|
end
|
|
|
|
else
|
|
|
|
luci.http.status(403, "Forbidden")
|
2008-06-28 16:03:54 +00:00
|
|
|
return
|
|
|
|
end
|
2008-12-14 21:43:10 +00:00
|
|
|
else
|
|
|
|
ctx.authsession = sess
|
2009-09-11 10:46:06 +00:00
|
|
|
ctx.authuser = user
|
2008-06-28 16:03:54 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if track.setgroup then
|
|
|
|
luci.sys.process.setgroup(track.setgroup)
|
|
|
|
end
|
|
|
|
|
|
|
|
if track.setuser then
|
|
|
|
luci.sys.process.setuser(track.setuser)
|
|
|
|
end
|
2008-05-24 22:58:45 +00:00
|
|
|
|
2009-01-04 15:45:57 +00:00
|
|
|
local target = nil
|
|
|
|
if c then
|
|
|
|
if type(c.target) == "function" then
|
|
|
|
target = c.target
|
|
|
|
elseif type(c.target) == "table" then
|
|
|
|
target = c.target.target
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if c and (c.index or type(target) == "function") then
|
2008-10-11 11:30:43 +00:00
|
|
|
ctx.dispatched = c
|
|
|
|
ctx.requested = ctx.requested or ctx.dispatched
|
|
|
|
end
|
|
|
|
|
2008-10-10 14:37:53 +00:00
|
|
|
if c and c.index then
|
|
|
|
local tpl = require "luci.template"
|
2008-10-11 11:30:43 +00:00
|
|
|
|
|
|
|
if util.copcall(tpl.render, "indexer", {}) then
|
2008-10-10 14:37:53 +00:00
|
|
|
return true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2009-01-04 15:45:57 +00:00
|
|
|
if type(target) == "function" then
|
2008-08-29 23:26:01 +00:00
|
|
|
util.copcall(function()
|
2009-01-04 15:45:57 +00:00
|
|
|
local oldenv = getfenv(target)
|
2008-09-05 18:35:09 +00:00
|
|
|
local module = require(c.module)
|
|
|
|
local env = setmetatable({}, {__index=
|
2008-09-23 00:10:51 +00:00
|
|
|
|
2008-09-05 18:35:09 +00:00
|
|
|
function(tbl, key)
|
2008-09-23 00:10:51 +00:00
|
|
|
return rawget(tbl, key) or module[key] or oldenv[key]
|
2008-09-05 18:35:09 +00:00
|
|
|
end})
|
|
|
|
|
2009-01-04 15:45:57 +00:00
|
|
|
setfenv(target, env)
|
2008-08-29 23:26:01 +00:00
|
|
|
end)
|
2008-09-23 00:10:51 +00:00
|
|
|
|
2011-07-21 01:04:53 +00:00
|
|
|
local ok, err
|
2009-01-04 15:45:57 +00:00
|
|
|
if type(c.target) == "table" then
|
2011-07-21 01:04:53 +00:00
|
|
|
ok, err = util.copcall(target, c.target, unpack(args))
|
2009-01-04 15:45:57 +00:00
|
|
|
else
|
2011-07-21 01:04:53 +00:00
|
|
|
ok, err = util.copcall(target, unpack(args))
|
2009-01-04 15:45:57 +00:00
|
|
|
end
|
2011-07-21 01:04:53 +00:00
|
|
|
assert(ok,
|
|
|
|
"Failed to execute " .. (type(c.target) == "function" and "function" or c.target.type or "unknown") ..
|
|
|
|
" dispatcher target for entry '/" .. table.concat(request, "/") .. "'.\n" ..
|
|
|
|
"The called action terminated with an exception:\n" .. tostring(err or "(unknown)"))
|
2008-03-21 19:30:53 +00:00
|
|
|
else
|
2011-07-20 23:57:32 +00:00
|
|
|
local root = node()
|
|
|
|
if not root or not root.target then
|
|
|
|
error404("No root node was registered, this usually happens if no module was installed.\n" ..
|
2011-10-21 17:22:48 +00:00
|
|
|
"Install luci-mod-admin-full and retry. " ..
|
2011-07-20 23:57:32 +00:00
|
|
|
"If the module is already installed, try removing the /tmp/luci-indexcache file.")
|
|
|
|
else
|
2011-07-21 01:04:53 +00:00
|
|
|
error404("No page is registered at '/" .. table.concat(request, "/") .. "'.\n" ..
|
2011-07-20 23:57:32 +00:00
|
|
|
"If this url belongs to an extension, make sure it is properly installed.\n" ..
|
|
|
|
"If the extension was recently installed, try removing the /tmp/luci-indexcache file.")
|
|
|
|
end
|
2008-03-02 21:52:58 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2008-07-29 20:32:02 +00:00
|
|
|
--- Generate the dispatching index using the best possible strategy.
|
2008-05-22 14:04:03 +00:00
|
|
|
function createindex()
|
2008-08-06 20:20:40 +00:00
|
|
|
local path = luci.util.libpath() .. "/controller/"
|
2009-04-04 22:54:16 +00:00
|
|
|
local suff = { ".lua", ".lua.gz" }
|
2008-09-23 00:10:51 +00:00
|
|
|
|
2008-06-14 14:12:12 +00:00
|
|
|
if luci.util.copcall(require, "luci.fastindex") then
|
2009-07-24 15:45:29 +00:00
|
|
|
createindex_fastindex(path, suff)
|
2008-06-02 15:36:05 +00:00
|
|
|
else
|
2009-07-24 15:45:29 +00:00
|
|
|
createindex_plain(path, suff)
|
2008-06-02 15:36:13 +00:00
|
|
|
end
|
2008-06-02 15:36:05 +00:00
|
|
|
end
|
2008-05-24 22:58:45 +00:00
|
|
|
|
2008-07-29 20:32:02 +00:00
|
|
|
--- Generate the dispatching index using the fastindex C-indexer.
|
|
|
|
-- @param path Controller base directory
|
2009-04-04 22:54:16 +00:00
|
|
|
-- @param suffixes Controller file suffixes
|
|
|
|
function createindex_fastindex(path, suffixes)
|
2009-07-24 15:45:29 +00:00
|
|
|
index = {}
|
2008-09-23 00:10:51 +00:00
|
|
|
|
2008-06-02 15:36:13 +00:00
|
|
|
if not fi then
|
|
|
|
fi = luci.fastindex.new("index")
|
2009-04-04 22:54:16 +00:00
|
|
|
for _, suffix in ipairs(suffixes) do
|
|
|
|
fi.add(path .. "*" .. suffix)
|
|
|
|
fi.add(path .. "*/*" .. suffix)
|
|
|
|
end
|
2008-06-02 15:36:13 +00:00
|
|
|
end
|
2008-05-26 09:45:12 +00:00
|
|
|
fi.scan()
|
2008-09-23 00:10:51 +00:00
|
|
|
|
2008-05-26 09:45:12 +00:00
|
|
|
for k, v in pairs(fi.indexes) do
|
2008-05-26 12:16:16 +00:00
|
|
|
index[v[2]] = v[1]
|
2008-05-26 09:45:12 +00:00
|
|
|
end
|
2008-06-02 15:36:05 +00:00
|
|
|
end
|
2008-05-26 09:45:12 +00:00
|
|
|
|
2008-07-29 20:32:02 +00:00
|
|
|
--- Generate the dispatching index using the native file-cache based strategy.
|
|
|
|
-- @param path Controller base directory
|
2009-04-04 22:54:16 +00:00
|
|
|
-- @param suffixes Controller file suffixes
|
|
|
|
function createindex_plain(path, suffixes)
|
|
|
|
local controllers = { }
|
|
|
|
for _, suffix in ipairs(suffixes) do
|
2009-07-19 00:24:58 +00:00
|
|
|
nixio.util.consume((fs.glob(path .. "*" .. suffix)), controllers)
|
|
|
|
nixio.util.consume((fs.glob(path .. "*/*" .. suffix)), controllers)
|
2009-04-04 22:54:16 +00:00
|
|
|
end
|
2008-11-05 14:10:02 +00:00
|
|
|
|
2008-08-29 23:26:01 +00:00
|
|
|
if indexcache then
|
2009-07-19 00:24:58 +00:00
|
|
|
local cachedate = fs.stat(indexcache, "mtime")
|
2008-11-05 14:10:02 +00:00
|
|
|
if cachedate then
|
|
|
|
local realdate = 0
|
|
|
|
for _, obj in ipairs(controllers) do
|
2011-07-18 14:50:39 +00:00
|
|
|
local omtime = fs.stat(obj, "mtime")
|
2008-11-05 14:10:02 +00:00
|
|
|
realdate = (omtime and omtime > realdate) and omtime or realdate
|
|
|
|
end
|
2008-09-01 16:05:34 +00:00
|
|
|
|
2008-11-05 14:10:02 +00:00
|
|
|
if cachedate > realdate then
|
|
|
|
assert(
|
|
|
|
sys.process.info("uid") == fs.stat(indexcache, "uid")
|
2009-06-21 13:42:26 +00:00
|
|
|
and fs.stat(indexcache, "modestr") == "rw-------",
|
2008-11-05 14:10:02 +00:00
|
|
|
"Fatal: Indexcache is not sane!"
|
|
|
|
)
|
2008-09-01 16:05:34 +00:00
|
|
|
|
2008-11-05 14:10:02 +00:00
|
|
|
index = loadfile(indexcache)()
|
|
|
|
return index
|
|
|
|
end
|
2008-09-23 00:10:51 +00:00
|
|
|
end
|
2008-08-29 23:26:01 +00:00
|
|
|
end
|
2008-09-23 00:10:51 +00:00
|
|
|
|
2009-07-24 15:45:29 +00:00
|
|
|
index = {}
|
2008-06-02 15:36:13 +00:00
|
|
|
|
2008-06-02 15:36:05 +00:00
|
|
|
for i,c in ipairs(controllers) do
|
2010-12-12 20:16:13 +00:00
|
|
|
local modname = "luci.controller." .. c:sub(#path+1, #c):gsub("/", ".")
|
2009-04-04 22:54:16 +00:00
|
|
|
for _, suffix in ipairs(suffixes) do
|
2010-12-12 20:16:13 +00:00
|
|
|
modname = modname:gsub(suffix.."$", "")
|
2009-04-04 22:54:16 +00:00
|
|
|
end
|
|
|
|
|
2010-12-12 20:16:13 +00:00
|
|
|
local mod = require(modname)
|
2011-07-21 01:04:53 +00:00
|
|
|
assert(mod ~= true,
|
|
|
|
"Invalid controller file found\n" ..
|
|
|
|
"The file '" .. c .. "' contains an invalid module line.\n" ..
|
|
|
|
"Please verify whether the module name is set to '" .. modname ..
|
|
|
|
"' - It must correspond to the file path!")
|
|
|
|
|
2008-08-29 23:26:01 +00:00
|
|
|
local idx = mod.index
|
2011-07-21 01:04:53 +00:00
|
|
|
assert(type(idx) == "function",
|
|
|
|
"Invalid controller file found\n" ..
|
|
|
|
"The file '" .. c .. "' contains no index() function.\n" ..
|
|
|
|
"Please make sure that the controller contains a valid " ..
|
|
|
|
"index function and verify the spelling!")
|
2008-09-23 00:10:51 +00:00
|
|
|
|
2011-07-21 01:04:53 +00:00
|
|
|
index[modname] = idx
|
2008-08-29 23:26:01 +00:00
|
|
|
end
|
2008-09-23 00:10:51 +00:00
|
|
|
|
2008-08-29 23:26:01 +00:00
|
|
|
if indexcache then
|
2009-06-21 13:42:26 +00:00
|
|
|
local f = nixio.open(indexcache, "w", 600)
|
|
|
|
f:writeall(util.get_bytecode(index))
|
|
|
|
f:close()
|
2008-06-02 15:36:05 +00:00
|
|
|
end
|
2008-03-21 19:30:53 +00:00
|
|
|
end
|
|
|
|
|
2008-07-29 20:32:02 +00:00
|
|
|
--- Create the dispatching tree from the index.
|
|
|
|
-- Build the index before if it does not exist yet.
|
2008-05-26 12:16:16 +00:00
|
|
|
function createtree()
|
2009-07-24 15:45:29 +00:00
|
|
|
if not index then
|
|
|
|
createindex()
|
2009-07-23 03:25:27 +00:00
|
|
|
end
|
2008-09-23 00:10:51 +00:00
|
|
|
|
2009-07-24 15:45:29 +00:00
|
|
|
local ctx = context
|
[PATCH] Allow smarter node creation based on visibility during createtree
As I've brought up on the mailing list thread "High latency caused by full tree creation", there is a large amount of delay per LuCI request which is spent building the node tree in createtree(). Most nodes created aren't
needed for the view presented to the user and only serve to consume memory and CPU time during a page load.
My idea is to provide an easy mechanism for index()ers to determine which needs to be created and what isn't. Due to the constraints of the standard LuCI web interface, this optimization needs to establish a few rules:
* The page requested must have its node created
* All parents of the page being requested must be created, so the children inherit the track
* All the top-level nodes "Status", "System", "Services", "Network" (and others added by extensions) must be created in order to have their top-level tabs in the UI
* All peers of second-level nodes need to be created as well for the same reason, to display their links on the subindexes
To make this easy to implement in each controller, the attached patch adds an "inreq" field to each created node to indicate if it lies on the request path. To satisfy the "top level node" requirement, we always
add the top level node, then check its inreq property if the top-level node is not "in request", then the controller can exit index() early.
2011-08-12 11:05:59 +00:00
|
|
|
local tree = {nodes={}, inreq=true}
|
2009-07-24 15:45:29 +00:00
|
|
|
local modi = {}
|
|
|
|
|
|
|
|
ctx.treecache = setmetatable({}, {__mode="v"})
|
|
|
|
ctx.tree = tree
|
2008-11-11 18:55:07 +00:00
|
|
|
ctx.modifiers = modi
|
2008-09-23 00:10:51 +00:00
|
|
|
|
2008-05-31 08:04:49 +00:00
|
|
|
-- Load default translation
|
2009-11-01 14:24:04 +00:00
|
|
|
require "luci.i18n".loadc("base")
|
2008-09-23 00:10:51 +00:00
|
|
|
|
2008-09-05 19:17:48 +00:00
|
|
|
local scope = setmetatable({}, {__index = luci.dispatcher})
|
2008-05-26 12:16:16 +00:00
|
|
|
|
2009-07-24 15:45:29 +00:00
|
|
|
for k, v in pairs(index) do
|
2008-05-31 08:04:49 +00:00
|
|
|
scope._NAME = k
|
|
|
|
setfenv(v, scope)
|
2009-07-24 15:45:29 +00:00
|
|
|
v()
|
2008-05-26 12:16:16 +00:00
|
|
|
end
|
2008-09-23 00:10:51 +00:00
|
|
|
|
2008-11-11 18:55:07 +00:00
|
|
|
local function modisort(a,b)
|
|
|
|
return modi[a].order < modi[b].order
|
|
|
|
end
|
|
|
|
|
|
|
|
for _, v in util.spairs(modi, modisort) do
|
|
|
|
scope._NAME = v.module
|
|
|
|
setfenv(v.func, scope)
|
2009-07-24 15:45:29 +00:00
|
|
|
v.func()
|
2008-11-11 18:55:07 +00:00
|
|
|
end
|
|
|
|
|
2009-07-24 15:45:29 +00:00
|
|
|
return tree
|
2008-05-26 12:16:16 +00:00
|
|
|
end
|
|
|
|
|
2008-11-11 18:55:07 +00:00
|
|
|
--- Register a tree modifier.
|
|
|
|
-- @param func Modifier function
|
|
|
|
-- @param order Modifier order value (optional)
|
|
|
|
function modifier(func, order)
|
|
|
|
context.modifiers[#context.modifiers+1] = {
|
|
|
|
func = func,
|
|
|
|
order = order or 0,
|
2008-11-29 21:21:43 +00:00
|
|
|
module
|
|
|
|
= getfenv(2)._NAME
|
2008-11-11 18:55:07 +00:00
|
|
|
}
|
|
|
|
end
|
|
|
|
|
2008-07-29 20:32:02 +00:00
|
|
|
--- Clone a node of the dispatching tree to another position.
|
|
|
|
-- @param path Virtual path destination
|
|
|
|
-- @param clone Virtual path source
|
|
|
|
-- @param title Destination node title (optional)
|
|
|
|
-- @param order Destination node order value (optional)
|
|
|
|
-- @return Dispatching tree node
|
2008-06-02 20:16:05 +00:00
|
|
|
function assign(path, clone, title, order)
|
2008-06-29 14:43:06 +00:00
|
|
|
local obj = node(unpack(path))
|
2008-06-02 20:16:05 +00:00
|
|
|
obj.nodes = nil
|
|
|
|
obj.module = nil
|
2008-09-23 00:10:51 +00:00
|
|
|
|
2008-06-02 20:16:05 +00:00
|
|
|
obj.title = title
|
|
|
|
obj.order = order
|
2008-06-06 15:50:21 +00:00
|
|
|
|
2008-09-15 16:50:55 +00:00
|
|
|
setmetatable(obj, {__index = _create_node(clone)})
|
2008-09-23 00:10:51 +00:00
|
|
|
|
2008-06-02 20:16:05 +00:00
|
|
|
return obj
|
|
|
|
end
|
2008-05-22 17:21:30 +00:00
|
|
|
|
2008-07-29 20:32:02 +00:00
|
|
|
--- Create a new dispatching node and define common parameters.
|
|
|
|
-- @param path Virtual path
|
2008-09-23 00:10:51 +00:00
|
|
|
-- @param target Target function to call when dispatched.
|
2008-07-29 20:32:02 +00:00
|
|
|
-- @param title Destination node title
|
|
|
|
-- @param order Destination node order value (optional)
|
|
|
|
-- @return Dispatching tree node
|
2008-06-02 20:16:05 +00:00
|
|
|
function entry(path, target, title, order)
|
2008-06-29 14:42:53 +00:00
|
|
|
local c = node(unpack(path))
|
2008-09-23 00:10:51 +00:00
|
|
|
|
2008-05-22 17:21:30 +00:00
|
|
|
c.target = target
|
|
|
|
c.title = title
|
|
|
|
c.order = order
|
2008-05-27 20:39:48 +00:00
|
|
|
c.module = getfenv(2)._NAME
|
2008-05-24 22:58:45 +00:00
|
|
|
|
2008-05-22 17:21:30 +00:00
|
|
|
return c
|
|
|
|
end
|
2008-05-04 20:53:31 +00:00
|
|
|
|
2009-02-26 17:08:41 +00:00
|
|
|
--- Fetch or create a dispatching node without setting the target module or
|
|
|
|
-- enabling the node.
|
|
|
|
-- @param ... Virtual path
|
|
|
|
-- @return Dispatching tree node
|
|
|
|
function get(...)
|
|
|
|
return _create_node({...})
|
|
|
|
end
|
|
|
|
|
2008-07-29 20:32:02 +00:00
|
|
|
--- Fetch or create a new dispatching node.
|
|
|
|
-- @param ... Virtual path
|
|
|
|
-- @return Dispatching tree node
|
2008-05-22 14:04:03 +00:00
|
|
|
function node(...)
|
2008-09-15 16:50:55 +00:00
|
|
|
local c = _create_node({...})
|
2008-05-24 22:58:45 +00:00
|
|
|
|
2008-06-06 15:50:21 +00:00
|
|
|
c.module = getfenv(2)._NAME
|
2008-07-17 16:02:29 +00:00
|
|
|
c.auto = nil
|
2008-06-04 22:41:58 +00:00
|
|
|
|
2008-05-22 14:04:03 +00:00
|
|
|
return c
|
|
|
|
end
|
|
|
|
|
2011-08-12 11:04:42 +00:00
|
|
|
function _create_node(path)
|
2008-08-29 23:26:01 +00:00
|
|
|
if #path == 0 then
|
|
|
|
return context.tree
|
|
|
|
end
|
2008-09-23 00:10:51 +00:00
|
|
|
|
2008-08-29 23:26:01 +00:00
|
|
|
local name = table.concat(path, ".")
|
2011-08-12 11:04:42 +00:00
|
|
|
local c = context.treecache[name]
|
2008-09-23 00:10:51 +00:00
|
|
|
|
2008-08-29 23:26:01 +00:00
|
|
|
if not c then
|
|
|
|
local last = table.remove(path)
|
2011-08-12 11:04:42 +00:00
|
|
|
local parent = _create_node(path)
|
2008-11-16 13:52:50 +00:00
|
|
|
|
2011-08-12 11:04:42 +00:00
|
|
|
c = {nodes={}, auto=true}
|
[PATCH] Allow smarter node creation based on visibility during createtree
As I've brought up on the mailing list thread "High latency caused by full tree creation", there is a large amount of delay per LuCI request which is spent building the node tree in createtree(). Most nodes created aren't
needed for the view presented to the user and only serve to consume memory and CPU time during a page load.
My idea is to provide an easy mechanism for index()ers to determine which needs to be created and what isn't. Due to the constraints of the standard LuCI web interface, this optimization needs to establish a few rules:
* The page requested must have its node created
* All parents of the page being requested must be created, so the children inherit the track
* All the top-level nodes "Status", "System", "Services", "Network" (and others added by extensions) must be created in order to have their top-level tabs in the UI
* All peers of second-level nodes need to be created as well for the same reason, to display their links on the subindexes
To make this easy to implement in each controller, the attached patch adds an "inreq" field to each created node to indicate if it lies on the request path. To satisfy the "top level node" requirement, we always
add the top level node, then check its inreq property if the top-level node is not "in request", then the controller can exit index() early.
2011-08-12 11:05:59 +00:00
|
|
|
-- the node is "in request" if the request path matches
|
|
|
|
-- at least up to the length of the node path
|
|
|
|
if parent.inreq and context.path[#path+1] == last then
|
|
|
|
c.inreq = true
|
|
|
|
end
|
2011-08-12 11:04:42 +00:00
|
|
|
parent.nodes[last] = c
|
|
|
|
context.treecache[name] = c
|
2008-08-29 23:26:01 +00:00
|
|
|
end
|
2011-08-12 11:04:42 +00:00
|
|
|
return c
|
2008-08-29 23:26:01 +00:00
|
|
|
end
|
|
|
|
|
2008-05-22 14:04:03 +00:00
|
|
|
-- Subdispatchers --
|
2008-07-29 20:32:02 +00:00
|
|
|
|
2011-10-25 22:48:43 +00:00
|
|
|
function _firstchild()
|
|
|
|
local path = { unpack(context.path) }
|
|
|
|
local name = table.concat(path, ".")
|
|
|
|
local node = context.treecache[name]
|
|
|
|
|
|
|
|
local lowest
|
|
|
|
if node and node.nodes and next(node.nodes) then
|
|
|
|
local k, v
|
|
|
|
for k, v in pairs(node.nodes) do
|
|
|
|
if not lowest or
|
|
|
|
(v.order or 100) < (node.nodes[lowest].order or 100)
|
|
|
|
then
|
|
|
|
lowest = k
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
assert(lowest ~= nil,
|
|
|
|
"The requested node contains no childs, unable to redispatch")
|
|
|
|
|
|
|
|
path[#path+1] = lowest
|
|
|
|
dispatch(path)
|
|
|
|
end
|
|
|
|
|
|
|
|
--- Alias the first (lowest order) page automatically
|
|
|
|
function firstchild()
|
|
|
|
return { type = "firstchild", target = _firstchild }
|
|
|
|
end
|
|
|
|
|
2008-07-29 20:32:02 +00:00
|
|
|
--- Create a redirect to another dispatching node.
|
|
|
|
-- @param ... Virtual path destination
|
2008-05-22 14:04:03 +00:00
|
|
|
function alias(...)
|
2008-10-30 19:09:52 +00:00
|
|
|
local req = {...}
|
|
|
|
return function(...)
|
|
|
|
for _, r in ipairs({...}) do
|
|
|
|
req[#req+1] = r
|
|
|
|
end
|
|
|
|
|
2008-06-14 14:12:12 +00:00
|
|
|
dispatch(req)
|
2008-05-04 20:53:31 +00:00
|
|
|
end
|
2008-03-21 19:30:53 +00:00
|
|
|
end
|
|
|
|
|
2008-07-29 20:32:02 +00:00
|
|
|
--- Rewrite the first x path values of the request.
|
|
|
|
-- @param n Number of path values to replace
|
|
|
|
-- @param ... Virtual path to replace removed path values with
|
2008-05-29 19:18:49 +00:00
|
|
|
function rewrite(n, ...)
|
2008-10-30 19:09:52 +00:00
|
|
|
local req = {...}
|
|
|
|
return function(...)
|
|
|
|
local dispatched = util.clone(context.dispatched)
|
|
|
|
|
2008-09-23 00:10:51 +00:00
|
|
|
for i=1,n do
|
2008-10-30 19:09:52 +00:00
|
|
|
table.remove(dispatched, 1)
|
|
|
|
end
|
|
|
|
|
|
|
|
for i, r in ipairs(req) do
|
|
|
|
table.insert(dispatched, i, r)
|
2008-05-29 19:18:49 +00:00
|
|
|
end
|
2008-09-23 00:10:51 +00:00
|
|
|
|
2008-10-30 19:09:52 +00:00
|
|
|
for _, r in ipairs({...}) do
|
|
|
|
dispatched[#dispatched+1] = r
|
2008-05-29 19:18:49 +00:00
|
|
|
end
|
2008-09-23 00:10:51 +00:00
|
|
|
|
2008-10-30 19:09:52 +00:00
|
|
|
dispatch(dispatched)
|
2008-05-29 19:18:49 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2009-01-04 15:45:57 +00:00
|
|
|
|
|
|
|
local function _call(self, ...)
|
2009-02-09 13:17:26 +00:00
|
|
|
if #self.argv > 0 then
|
2009-01-04 15:45:57 +00:00
|
|
|
return getfenv()[self.name](unpack(self.argv), ...)
|
|
|
|
else
|
|
|
|
return getfenv()[self.name](...)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2008-07-29 20:32:02 +00:00
|
|
|
--- Create a function-call dispatching target.
|
2008-09-23 00:10:51 +00:00
|
|
|
-- @param name Target function of local controller
|
2008-07-29 20:32:02 +00:00
|
|
|
-- @param ... Additional parameters passed to the function
|
2008-06-14 14:12:12 +00:00
|
|
|
function call(name, ...)
|
2009-01-04 15:45:57 +00:00
|
|
|
return {type = "call", argv = {...}, name = name, target = _call}
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
local _template = function(self, ...)
|
|
|
|
require "luci.template".render(self.view)
|
2008-05-27 20:39:48 +00:00
|
|
|
end
|
|
|
|
|
2008-07-29 20:32:02 +00:00
|
|
|
--- Create a template render dispatching target.
|
2008-07-29 21:16:12 +00:00
|
|
|
-- @param name Template to be rendered
|
2008-05-22 14:04:03 +00:00
|
|
|
function template(name)
|
2009-01-04 15:45:57 +00:00
|
|
|
return {type = "template", view = name, target = _template}
|
2008-05-22 14:04:03 +00:00
|
|
|
end
|
2008-05-04 20:53:31 +00:00
|
|
|
|
2008-08-29 20:36:45 +00:00
|
|
|
|
2009-01-04 15:45:57 +00:00
|
|
|
local function _cbi(self, ...)
|
|
|
|
local cbi = require "luci.cbi"
|
|
|
|
local tpl = require "luci.template"
|
|
|
|
local http = require "luci.http"
|
2008-05-24 22:58:45 +00:00
|
|
|
|
2009-01-04 15:45:57 +00:00
|
|
|
local config = self.config or {}
|
|
|
|
local maps = cbi.load(self.model, ...)
|
2008-10-20 22:35:11 +00:00
|
|
|
|
2009-01-04 15:45:57 +00:00
|
|
|
local state = nil
|
2008-05-24 22:58:45 +00:00
|
|
|
|
2009-01-04 15:45:57 +00:00
|
|
|
for i, res in ipairs(maps) do
|
2009-04-03 18:08:25 +00:00
|
|
|
res.flow = config
|
2009-01-04 15:45:57 +00:00
|
|
|
local cstate = res:parse()
|
2009-03-07 16:28:27 +00:00
|
|
|
if cstate and (not state or cstate < state) then
|
2009-01-04 15:45:57 +00:00
|
|
|
state = cstate
|
2008-11-05 21:12:19 +00:00
|
|
|
end
|
2009-01-04 15:45:57 +00:00
|
|
|
end
|
2008-11-05 21:12:19 +00:00
|
|
|
|
2009-06-20 07:14:36 +00:00
|
|
|
local function _resolve_path(path)
|
|
|
|
return type(path) == "table" and build_url(unpack(path)) or path
|
|
|
|
end
|
|
|
|
|
2009-01-04 15:45:57 +00:00
|
|
|
if config.on_valid_to and state and state > 0 and state < 2 then
|
2009-06-20 07:14:36 +00:00
|
|
|
http.redirect(_resolve_path(config.on_valid_to))
|
2009-01-04 15:45:57 +00:00
|
|
|
return
|
|
|
|
end
|
2008-11-02 13:26:41 +00:00
|
|
|
|
2009-01-04 15:45:57 +00:00
|
|
|
if config.on_changed_to and state and state > 1 then
|
2009-06-20 07:14:36 +00:00
|
|
|
http.redirect(_resolve_path(config.on_changed_to))
|
2009-01-04 15:45:57 +00:00
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
if config.on_success_to and state and state > 0 then
|
2009-06-20 07:14:36 +00:00
|
|
|
http.redirect(_resolve_path(config.on_success_to))
|
2009-01-04 15:45:57 +00:00
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
if config.state_handler then
|
|
|
|
if not config.state_handler(state, maps) then
|
|
|
|
return
|
2008-11-01 18:32:02 +00:00
|
|
|
end
|
2009-01-04 15:45:57 +00:00
|
|
|
end
|
2008-11-01 18:32:02 +00:00
|
|
|
|
2009-01-04 15:45:57 +00:00
|
|
|
http.header("X-CBI-State", state or 0)
|
2010-11-22 00:32:54 +00:00
|
|
|
|
2009-03-27 00:10:17 +00:00
|
|
|
if not config.noheader then
|
|
|
|
tpl.render("cbi/header", {state = state})
|
|
|
|
end
|
2010-11-22 00:32:54 +00:00
|
|
|
|
|
|
|
local redirect
|
2010-11-27 18:17:15 +00:00
|
|
|
local messages
|
2010-11-22 00:32:54 +00:00
|
|
|
local applymap = false
|
|
|
|
local pageaction = true
|
|
|
|
local parsechain = { }
|
|
|
|
|
2009-01-04 15:45:57 +00:00
|
|
|
for i, res in ipairs(maps) do
|
2010-11-22 00:32:54 +00:00
|
|
|
if res.apply_needed and res.parsechain then
|
|
|
|
local c
|
|
|
|
for _, c in ipairs(res.parsechain) do
|
|
|
|
parsechain[#parsechain+1] = c
|
|
|
|
end
|
|
|
|
applymap = true
|
2008-07-15 13:17:28 +00:00
|
|
|
end
|
2010-11-22 00:32:54 +00:00
|
|
|
|
2010-11-21 00:14:03 +00:00
|
|
|
if res.redirect then
|
|
|
|
redirect = redirect or res.redirect
|
|
|
|
end
|
2010-11-22 00:32:54 +00:00
|
|
|
|
|
|
|
if res.pageaction == false then
|
|
|
|
pageaction = false
|
|
|
|
end
|
2010-11-27 18:17:15 +00:00
|
|
|
|
|
|
|
if res.message then
|
|
|
|
messages = messages or { }
|
|
|
|
messages[#messages+1] = res.message
|
|
|
|
end
|
2008-03-02 21:52:58 +00:00
|
|
|
end
|
2010-11-22 00:32:54 +00:00
|
|
|
|
|
|
|
for i, res in ipairs(maps) do
|
|
|
|
res:render({
|
|
|
|
firstmap = (i == 1),
|
|
|
|
applymap = applymap,
|
|
|
|
redirect = redirect,
|
2010-11-27 18:17:15 +00:00
|
|
|
messages = messages,
|
2010-11-22 00:32:54 +00:00
|
|
|
pageaction = pageaction,
|
|
|
|
parsechain = parsechain
|
|
|
|
})
|
|
|
|
end
|
|
|
|
|
2009-03-27 00:10:17 +00:00
|
|
|
if not config.nofooter then
|
2010-11-21 00:14:03 +00:00
|
|
|
tpl.render("cbi/footer", {
|
|
|
|
flow = config,
|
|
|
|
pageaction = pageaction,
|
|
|
|
redirect = redirect,
|
|
|
|
state = state,
|
|
|
|
autoapply = config.autoapply
|
|
|
|
})
|
2009-03-27 00:10:17 +00:00
|
|
|
end
|
2008-05-24 22:58:45 +00:00
|
|
|
end
|
2008-08-09 14:14:04 +00:00
|
|
|
|
2009-01-04 15:45:57 +00:00
|
|
|
--- Create a CBI model dispatching target.
|
|
|
|
-- @param model CBI model to be rendered
|
|
|
|
function cbi(model, config)
|
|
|
|
return {type = "cbi", config = config, model = model, target = _cbi}
|
|
|
|
end
|
2008-08-29 20:36:45 +00:00
|
|
|
|
2008-08-09 14:14:04 +00:00
|
|
|
|
2009-01-04 15:45:57 +00:00
|
|
|
local function _arcombine(self, ...)
|
|
|
|
local argv = {...}
|
|
|
|
local target = #argv > 0 and self.targets[2] or self.targets[1]
|
|
|
|
setfenv(target.target, self.env)
|
|
|
|
target:target(unpack(argv))
|
|
|
|
end
|
2008-10-20 22:35:11 +00:00
|
|
|
|
2009-01-04 15:45:57 +00:00
|
|
|
--- Create a combined dispatching target for non argv and argv requests.
|
|
|
|
-- @param trg1 Overview Target
|
|
|
|
-- @param trg2 Detail Target
|
|
|
|
function arcombine(trg1, trg2)
|
|
|
|
return {type = "arcombine", env = getfenv(), target = _arcombine, targets = {trg1, trg2}}
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
local function _form(self, ...)
|
|
|
|
local cbi = require "luci.cbi"
|
|
|
|
local tpl = require "luci.template"
|
|
|
|
local http = require "luci.http"
|
2008-08-09 14:14:04 +00:00
|
|
|
|
2009-01-04 15:45:57 +00:00
|
|
|
local maps = luci.cbi.load(self.model, ...)
|
|
|
|
local state = nil
|
|
|
|
|
|
|
|
for i, res in ipairs(maps) do
|
|
|
|
local cstate = res:parse()
|
2009-03-07 16:28:27 +00:00
|
|
|
if cstate and (not state or cstate < state) then
|
2009-01-04 15:45:57 +00:00
|
|
|
state = cstate
|
2008-08-09 14:14:04 +00:00
|
|
|
end
|
|
|
|
end
|
2009-01-04 15:45:57 +00:00
|
|
|
|
|
|
|
http.header("X-CBI-State", state or 0)
|
|
|
|
tpl.render("header")
|
|
|
|
for i, res in ipairs(maps) do
|
|
|
|
res:render()
|
|
|
|
end
|
|
|
|
tpl.render("footer")
|
|
|
|
end
|
|
|
|
|
|
|
|
--- Create a CBI form model dispatching target.
|
|
|
|
-- @param model CBI form model tpo be rendered
|
|
|
|
function form(model)
|
|
|
|
return {type = "cbi", model = model, target = _form}
|
2008-08-09 14:14:04 +00:00
|
|
|
end
|
2011-08-12 11:13:39 +00:00
|
|
|
|
|
|
|
--- Access the luci.i18n translate() api.
|
|
|
|
-- @class function
|
|
|
|
-- @name translate
|
|
|
|
-- @param text Text to translate
|
|
|
|
translate = i18n.translate
|
2011-08-12 13:11:29 +00:00
|
|
|
|
|
|
|
--- No-op function used to mark translation entries for menu labels.
|
|
|
|
-- This function does not actually translate the given argument but
|
|
|
|
-- is used by build/i18n-scan.pl to find translatable entries.
|
|
|
|
function _(text)
|
|
|
|
return text
|
|
|
|
end
|