* Core translation part 1
[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 or c.leaf 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
132 if c and type(c.target) == "function" then
133 dispatched = c
134 stat, mod = pcall(require, c.module)
135 if stat then
136 luci.util.updfenv(c.target, mod)
137 end
138
139 stat, err = pcall(c.target)
140 if not stat then
141 error500(err)
142 end
143 else
144 error404()
145 end
146 end
147
148 -- Generates the dispatching tree
149 function createindex()
150 index = {}
151 local path = luci.sys.libpath() .. "/controller/"
152 local suff = ".lua"
153
154 --[[if pcall(require, "fastindex") then
155 createindex_fastindex(path, suff)
156 else
157 createindex_plain(path, suff)
158 end]]--
159
160 createindex_plain(path, suff)
161
162 built_index = true
163 end
164
165 -- Uses fastindex to create the dispatching tree
166 function createindex_fastindex(path, suffix)
167 local fi = fastindex.new("index")
168 fi.add(path .. "*" .. suffix)
169 fi.add(path .. "*/*" .. suffix)
170 fi.scan()
171
172 for k, v in pairs(fi.indexes) do
173 index[v[2]] = v[1]
174 end
175 end
176
177 -- Calls the index function of all available controllers
178 function createindex_plain(path, suffix)
179 local cachetime = nil
180
181 local controllers = luci.util.combine(
182 luci.fs.glob(path .. "*" .. suffix) or {},
183 luci.fs.glob(path .. "*/*" .. suffix) or {}
184 )
185
186 if indexcache then
187 cachetime = luci.fs.mtime(indexcache)
188
189 if not cachetime then
190 luci.fs.mkdir(indexcache)
191 luci.fs.chmod(indexcache, "a=,u=rwx")
192 end
193 end
194
195 if not cachetime then
196 for i,c in ipairs(controllers) do
197 c = "luci.controller." .. c:sub(#path+1, #c-#suffix):gsub("/", ".")
198 stat, mod = pcall(require, c)
199
200 if stat and mod and type(mod.index) == "function" then
201 index[c] = mod.index
202
203 if indexcache then
204 luci.fs.writefile(indexcache .. "/" .. c, string.dump(mod.index))
205 end
206 end
207 end
208 else
209 for i,c in ipairs(luci.fs.dir(indexcache)) do
210 if c:sub(1) ~= "." then
211 index[c] = loadfile(indexcache .. "/" .. c)
212 end
213 end
214 end
215 end
216
217 -- Creates the dispatching tree from the index
218 function createtree()
219 if not built_index then
220 createindex()
221 end
222
223 require("luci.i18n")
224
225 -- Load default translation
226 luci.i18n.loadc("default")
227
228 local scope = _G
229 for k,v in pairs(_M) do
230 if type(v) == "function" then
231 scope[k] = v
232 end
233 end
234
235 for k, v in pairs(index) do
236 scope._NAME = k
237 setfenv(v, scope)
238
239 local stat, err = pcall(v)
240 if not stat then
241 error500(err)
242 os.exit(1)
243 end
244 end
245
246 built_tree = true
247 end
248
249 -- Shortcut for creating a dispatching node
250 function entry(path, target, title, order, add)
251 add = add or {}
252
253 local c = node(path)
254 c.target = target
255 c.title = title
256 c.order = order
257 c.module = getfenv(2)._NAME
258
259 for k,v in pairs(add) do
260 c[k] = v
261 end
262
263 return c
264 end
265
266 -- Fetch a dispatching node
267 function node(...)
268 local c = tree
269
270 if arg[1] and type(arg[1]) == "table" then
271 arg = arg[1]
272 end
273
274 for k,v in ipairs(arg) do
275 if not c.nodes[v] then
276 c.nodes[v] = {nodes={}, module=getfenv(2)._NAME}
277 end
278
279 c = c.nodes[v]
280 end
281
282 return c
283 end
284
285 -- Subdispatchers --
286 function alias(...)
287 local req = arg
288 return function()
289 request = req
290 dispatch()
291 end
292 end
293
294 function rewrite(n, ...)
295 local req = arg
296 return function()
297 for i=1,n do
298 table.remove(request, 1)
299 end
300
301 for i,r in ipairs(req) do
302 table.insert(request, i, r)
303 end
304
305 dispatch()
306 end
307 end
308
309 function call(name)
310 return function() getfenv()[name]() end
311 end
312
313 function template(name)
314 require("luci.template")
315 return function() luci.template.render(name) end
316 end
317
318 function cbi(model)
319 require("luci.cbi")
320 require("luci.template")
321
322 return function()
323 local stat, res = pcall(luci.cbi.load, model)
324 if not stat then
325 error500(res)
326 return true
327 end
328
329 local stat, err = pcall(res.parse, res)
330 if not stat then
331 error500(err)
332 return true
333 end
334
335 luci.template.render("cbi/header")
336 res:render()
337 luci.template.render("cbi/footer")
338 end
339 end