commit 4f6198094cf4134179d1f9c9fa8f79759a27c87e
[project/luci.git] / libs / web / luasrc / dispatcher.lua
1 --[[
2 LuCI - Dispatcher
3
4 Description:
5 The request dispatcher and module dispatcher generators
6
7 FileId:
8 $Id$
9
10 License:
11 Copyright 2008 Steven Barth <steven@midlink.org>
12
13 Licensed under the Apache License, Version 2.0 (the "License");
14 you may not use this file except in compliance with the License.
15 You may obtain a copy of the License at
16
17 http://www.apache.org/licenses/LICENSE-2.0
18
19 Unless required by applicable law or agreed to in writing, software
20 distributed under the License is distributed on an "AS IS" BASIS,
21 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22 See the License for the specific language governing permissions and
23 limitations under the License.
24
25 ]]--
26 module("luci.dispatcher", package.seeall)
27 require("luci.http")
28 require("luci.sys")
29 require("luci.fs")
30
31 -- Local dispatch database
32 local tree = {nodes={}}
33
34 -- Index table
35 local index = {}
36
37 -- Global request object
38 request = {}
39
40 -- Active dispatched node
41 dispatched = nil
42
43 -- Status fields
44 built_index = false
45 built_tree = false
46
47
48 -- Builds a URL
49 function build_url(...)
50 return luci.http.dispatcher() .. "/" .. table.concat(arg, "/")
51 end
52
53 -- Sends a 404 error code and renders the "error404" template if available
54 function error404(message)
55 luci.http.status(404, "Not Found")
56 message = message or "Not Found"
57
58 require("luci.template")
59 if not pcall(luci.template.render, "error404") then
60 luci.http.prepare_content("text/plain")
61 print(message)
62 end
63 return false
64 end
65
66 -- Sends a 500 error code and renders the "error500" template if available
67 function error500(message)
68 luci.http.status(500, "Internal Server Error")
69
70 require("luci.template")
71 if not pcall(luci.template.render, "error500", {message=message}) then
72 luci.http.prepare_content("text/plain")
73 print(message)
74 end
75 return false
76 end
77
78 -- Creates a request object for dispatching
79 function httpdispatch()
80 local pathinfo = luci.http.env.PATH_INFO or ""
81 local c = tree
82
83 for s in pathinfo:gmatch("([%w_]+)") do
84 table.insert(request, s)
85 end
86
87 dispatch()
88 end
89
90 -- Dispatches a request
91 function dispatch()
92 if not built_tree then
93 createtree()
94 end
95
96 local c = tree
97 local track = {}
98
99 for i, s in ipairs(request) do
100 c = c.nodes[s]
101 if not c then
102 break
103 end
104
105 for k, v in pairs(c) do
106 track[k] = v
107 end
108 end
109
110
111 if track.i18n then
112 require("luci.i18n").loadc(track.i18n)
113 end
114
115 if track.setgroup then
116 luci.sys.process.setgroup(track.setgroup)
117 end
118
119 if track.setuser then
120 luci.sys.process.setuser(track.setuser)
121 end
122
123 -- Init template engine
124 local tpl = require("luci.template")
125 tpl.viewns.translate = function(...) return require("luci.i18n").translate(...) end
126 tpl.viewns.controller = luci.http.dispatcher()
127 tpl.viewns.uploadctrl = luci.http.dispatcher_upload()
128 tpl.viewns.media = luci.config.main.mediaurlbase
129 tpl.viewns.resource = luci.config.main.resourcebase
130
131 -- Load default translation
132 require("luci.i18n").loadc("default")
133
134
135 if c and type(c.target) == "function" then
136 dispatched = c
137
138 stat, err = pcall(c.target)
139 if not stat then
140 error500(err)
141 end
142 else
143 error404()
144 end
145 end
146
147 -- Generates the dispatching tree
148 function createindex()
149 index = {}
150 local path = luci.sys.libpath() .. "/controller/"
151 local suff = ".lua"
152
153 if pcall(require, "fastindex") then
154 createindex_fastindex(path, suff)
155 else
156 createindex_plain(path, suff)
157 end
158
159 built_index = true
160 end
161
162 -- Uses fastindex to create the dispatching tree
163 function createindex_fastindex(path, suffix)
164 local fi = fastindex.new("index")
165 fi.add(path .. "*" .. suffix)
166 fi.add(path .. "*/*" .. suffix)
167 fi.scan()
168
169 for k, v in pairs(fi.indexes) do
170 index[v[2]] = v[1]
171 end
172 end
173
174 -- Calls the index function of all available controllers
175 function createindex_plain(path, suffix)
176 local controllers = luci.util.combine(
177 luci.fs.glob(path .. "*" .. suffix) or {},
178 luci.fs.glob(path .. "*/*" .. suffix) or {}
179 )
180
181 for i,c in ipairs(controllers) do
182 c = "luci.controller." .. c:sub(#path+1, #c-#suffix):gsub("/", ".")
183 stat, mod = pcall(require, c)
184
185 if stat and mod and type(mod.index) == "function" then
186 index[c] = mod.index
187 end
188 end
189 end
190
191 -- Creates the dispatching tree from the index
192 function createtree()
193 if not built_index then
194 createindex()
195 end
196
197 for k, v in pairs(index) do
198 luci.util.updfenv(v, _M)
199
200 local stat, mod = pcall(require, k)
201 if stat then
202 luci.util.updfenv(v, mod)
203 end
204
205 pcall(v)
206 end
207
208 built_tree = true
209 end
210
211 -- Shortcut for creating a dispatching node
212 function entry(path, target, title, order, add)
213 add = add or {}
214
215 local c = node(path)
216 c.target = target
217 c.title = title
218 c.order = order
219
220 for k,v in pairs(add) do
221 c[k] = v
222 end
223
224 return c
225 end
226
227 -- Fetch a dispatching node
228 function node(...)
229 local c = tree
230
231 if arg[1] and type(arg[1]) == "table" then
232 arg = arg[1]
233 end
234
235 for k,v in ipairs(arg) do
236 if not c.nodes[v] then
237 c.nodes[v] = {nodes={}}
238 end
239
240 c = c.nodes[v]
241 end
242
243 return c
244 end
245
246 -- Subdispatchers --
247 function alias(...)
248 local req = arg
249 return function()
250 request = req
251 dispatch()
252 end
253 end
254
255 function template(name)
256 require("luci.template")
257 return function() luci.template.render(name) end
258 end
259
260 function cbi(model)
261 require("luci.cbi")
262 require("luci.template")
263
264 return function()
265 local stat, res = pcall(luci.cbi.load, model)
266 if not stat then
267 error500(res)
268 return true
269 end
270
271 local stat, err = pcall(res.parse, res)
272 if not stat then
273 error500(err)
274 return true
275 end
276
277 luci.template.render("cbi/header")
278 res:render()
279 luci.template.render("cbi/footer")
280 end
281 end