[PATCH] Allow smarter node creation based on visibility during createtree
[project/luci.git] / libs / web / luasrc / dispatcher.lua
index 589cb73..a4ed4b8 100644 (file)
@@ -130,6 +130,8 @@ function httpdispatch(request, prefix)
 
        local r = {}
        context.request = r
+       context.urltoken = {}
+       
        local pathinfo = http.urldecode(request:getenv("PATH_INFO") or "", true)
 
        if prefix then
@@ -138,8 +140,18 @@ function httpdispatch(request, prefix)
                end
        end
 
+       local tokensok = true
        for node in pathinfo:gmatch("[^/]+") do
-               r[#r+1] = node
+               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
        end
 
        local stat, err = util.coxpcall(function()
@@ -157,7 +169,6 @@ function dispatch(request)
        --context._disable_memtrace = require "luci.debug".trap_memtrace("l")
        local ctx = context
        ctx.path = request
-       ctx.urltoken   = ctx.urltoken or {}
 
        local conf = require "luci.config"
        assert(conf.main,
@@ -187,34 +198,23 @@ function dispatch(request)
        ctx.args = args
        ctx.requestargs = ctx.requestargs or args
        local n
-       local t = true
        local token = ctx.urltoken
        local preq = {}
        local freq = {}
 
        for i, s in ipairs(request) do
-               local tkey, tval
-               if t then
-                       tkey, tval = s:match(";(%w+)=([a-fA-F0-9]*)")
+               preq[#preq+1] = s
+               freq[#freq+1] = s
+               c = c.nodes[s]
+               n = i
+               if not c then
+                       break
                end
 
-               if tkey then
-                       token[tkey] = tval
-               else
-                       t = false
-                       preq[#preq+1] = s
-                       freq[#freq+1] = s
-                       c = c.nodes[s]
-                       n = i
-                       if not c then
-                               break
-                       end
-
-                       util.update(track, c)
+               util.update(track, c)
 
-                       if c.leaf then
-                               break
-                       end
+               if c.leaf then
+                       break
                end
        end
 
@@ -269,7 +269,12 @@ function dispatch(request)
        end
 
        track.dependent = (track.dependent ~= false)
-       assert(not track.dependent or not track.auto, "Access Violation")
+       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"
+       )
 
        if track.sysauth then
                local sauth = require "luci.sauth"
@@ -380,13 +385,27 @@ function dispatch(request)
                        setfenv(target, env)
                end)
 
+               local ok, err
                if type(c.target) == "table" then
-                       target(c.target, unpack(args))
+                       ok, err = util.copcall(target, c.target, unpack(args))
                else
-                       target(unpack(args))
+                       ok, err = util.copcall(target, unpack(args))
                end
+               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)"))
        else
-               error404()
+               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" ..
+                                "Install luci-admin-full and retry. " ..
+                                "If the module is already installed, try removing the /tmp/luci-indexcache file.")
+               else
+                       error404("No page is registered at '/" .. table.concat(request, "/") .. "'.\n" ..
+                                "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
        end
 end
 
@@ -437,7 +456,7 @@ function createindex_plain(path, suffixes)
                if cachedate then
                        local realdate = 0
                        for _, obj in ipairs(controllers) do
-                               local omtime = fs.stat(path .. "/" .. obj, "mtime")
+                               local omtime = fs.stat(obj, "mtime")
                                realdate = (omtime and omtime > realdate) and omtime or realdate
                        end
 
@@ -457,17 +476,26 @@ function createindex_plain(path, suffixes)
        index = {}
 
        for i,c in ipairs(controllers) do
-               local module = "luci.controller." .. c:sub(#path+1, #c):gsub("/", ".")
+               local modname = "luci.controller." .. c:sub(#path+1, #c):gsub("/", ".")
                for _, suffix in ipairs(suffixes) do
-                       module = module:gsub(suffix.."$", "")
+                       modname = modname:gsub(suffix.."$", "")
                end
 
-               local mod = require(module)
+               local mod = require(modname)
+               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!")
+               
                local idx = mod.index
+               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!")
 
-               if type(idx) == "function" then
-                       index[module] = idx
-               end
+               index[modname] = idx
        end
 
        if indexcache then
@@ -485,7 +513,7 @@ function createtree()
        end
 
        local ctx  = context
-       local tree = {nodes={}}
+       local tree = {nodes={}, inreq=true}
        local modi = {}
 
        ctx.treecache = setmetatable({}, {__mode="v"})
@@ -584,28 +612,28 @@ function node(...)
        return c
 end
 
-function _create_node(path, cache)
+function _create_node(path)
        if #path == 0 then
                return context.tree
        end
 
-       cache = cache or context.treecache
        local name = table.concat(path, ".")
-       local c = cache[name]
+       local c = context.treecache[name]
 
        if not c then
-               local new = {nodes={}, auto=true, path=util.clone(path)}
                local last = table.remove(path)
+               local parent = _create_node(path)
 
-               c = _create_node(path, cache)
-
-               c.nodes[last] = new
-               cache[name] = new
-
-               return new
-       else
-               return c
+               c = {nodes={}, auto=true}
+               -- 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
+               parent.nodes[last] = c
+               context.treecache[name] = c
        end
+       return c
 end
 
 -- Subdispatchers --
@@ -725,6 +753,7 @@ local function _cbi(self, ...)
        end
 
        local redirect
+       local messages
        local applymap   = false
        local pageaction = true
        local parsechain = { }
@@ -745,6 +774,11 @@ local function _cbi(self, ...)
                if res.pageaction == false then
                        pageaction = false
                end
+
+               if res.message then
+                       messages = messages or { }
+                       messages[#messages+1] = res.message
+               end
        end
 
        for i, res in ipairs(maps) do
@@ -752,6 +786,7 @@ local function _cbi(self, ...)
                        firstmap   = (i == 1),
                        applymap   = applymap,
                        redirect   = redirect,
+                       messages   = messages,
                        pageaction = pageaction,
                        parsechain = parsechain
                })