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