webmcp

view framework/cgi-bin/webmcp.lua @ 0:9fdfb27f8e67

Version 1.0.0
author jbe/bsw
date Sun Oct 25 12:00:00 2009 +0100 (2009-10-25)
parents
children 985024b16520
line source
1 #!/usr/bin/env lua
3 -- include "../lib/" in search path for libraries
4 do
5 package.path = '../lib/?.lua;' .. package.path
6 -- find out which file name extension shared libraries have
7 local slib_exts = {}
8 for ext in string.gmatch(package.cpath, "%?%.([A-Za-z0-9_-]+)") do
9 slib_exts[ext] = true
10 end
11 local paths = {}
12 for ext in pairs(slib_exts) do
13 paths[#paths+1] = "../accelerator/?." .. ext
14 end
15 for ext in pairs(slib_exts) do
16 paths[#paths+1] = "../lib/?." .. ext
17 end
18 paths[#paths+1] = package.cpath
19 package.cpath = table.concat(paths, ";")
20 end
22 -- load os extensions for lua
23 -- (should happen as soon as possible due to run time measurement)
24 require 'extos'
26 -- load nihil library
27 require 'nihil'
29 -- load random generator library
30 require 'multirand'
32 -- load rocketcgi library and map it to cgi
33 do
34 local option = os.getenv("WEBMCP_INTERACTIVE")
35 if option and option ~= "" and option ~= "0" then
36 cgi = nil
37 else
38 require 'rocketcgi'
39 cgi = rocketcgi
40 end
41 end
43 -- load database access library with object relational mapper
44 require 'mondelefant'
45 mondelefant.connection_prototype.error_objects = true
47 -- load type system "atom"
48 require 'atom'
50 -- load mondelefant atom connector
51 require 'mondelefant_atom_connector'
53 --[[--
54 cloned_table = -- newly generated table
55 table.new(
56 table_or_nil -- keys of a given table will be copied to the new table
57 )
59 If a table is given, then a cloned table is returned.
60 If nil is given, then a new empty table is returned.
62 --]]--
63 function table.new(tbl)
64 new_tbl = {}
65 if tbl then
66 for key, value in pairs(tbl) do
67 new_tbl[key] = value
68 end
69 end
70 return new_tbl
71 end
72 --//--
74 --[[--
75 at_exit(
76 func -- function to be called before the process is ending
77 )
79 Registers a function to be called before the CGI process is exiting.
80 --]]--
81 do
82 local exit_handlers = {}
83 function at_exit(func)
84 table.insert(exit_handlers, func)
85 end
86 function exit(code)
87 for i = #exit_handlers, 1, -1 do
88 exit_handlers[i]()
89 end
90 os.exit(code)
91 end
92 end
93 --//--
95 --[[--
96 app -- table to store an application state
98 'app' is a global table for storing any application state data
99 --]]--
100 app = {}
101 --//--
103 --[[--
104 config -- table to store application configuration
106 'config' is a global table, which can be modified by a config file of an application to modify the behaviour of that application.
107 --]]--
108 config = {}
109 --//--
111 -- autoloader system for WebMCP environment "../env/",
112 -- application environment extensions "$WEBMCP_APP_BASE/env/"
113 -- and models "$WEBMCP_APP_BASE/model/"
114 do
115 local app_base = os.getenv("WEBMCP_APP_BASEPATH")
116 if not app_base then
117 error(
118 "Failed to initialize autoloader " ..
119 "due to unset WEBMCP_APP_BASEPATH environment variable."
120 )
121 end
122 local weakkey_mt = { __mode = "k" }
123 local autoloader_category = setmetatable({}, weakkey_mt)
124 local autoloader_path = setmetatable({}, weakkey_mt)
125 local autoloader_mt = {}
126 local function install_autoloader(self, category, path)
127 autoloader_category[self] = category
128 autoloader_path[self] = path
129 setmetatable(self, autoloader_mt)
130 end
131 local function try_exec(filename)
132 local file = io.open(filename, "r")
133 if file then
134 local filedata = file:read("*a")
135 io.close(file)
136 local func, errmsg = loadstring(filedata, "=" .. filename)
137 if func then
138 func()
139 return true
140 else
141 error(errmsg, 0)
142 end
143 else
144 return false
145 end
146 end
147 local function compose_path_string(base, path, key)
148 return string.gsub(
149 base .. table.concat(path, "/") .. "/" .. key, "/+", "/"
150 )
151 end
152 function autoloader_mt.__index(self, key)
153 local category, base_path, merge_base_path, file_key
154 local merge = false
155 if
156 string.find(key, "^[a-z_][A-Za-z0-9_]*$") and
157 not string.find(key, "^__")
158 then
159 category = "env"
160 base_path = "../env/"
161 merge = true
162 merge_base_path = app_base .. "/env/"
163 file_key = key
164 elseif string.find(key, "^[A-Z][A-Za-z0-9]*$") then
165 category = "model"
166 base_path = app_base .. "/model/"
167 local first = true
168 file_key = string.gsub(key, "[A-Z]",
169 function(c)
170 if first then
171 first = false
172 return string.lower(c)
173 else
174 return "_" .. string.lower(c)
175 end
176 end
177 )
178 else
179 return
180 end
181 local required_category = autoloader_category[self]
182 if required_category and required_category ~= category then return end
183 local path = autoloader_path[self]
184 local path_string = compose_path_string(base_path, path, file_key)
185 local merge_path_string
186 if merge then
187 merge_path_string = compose_path_string(
188 merge_base_path, path, file_key
189 )
190 end
191 local function try_dir(dirname)
192 local dir = io.open(dirname)
193 if dir then
194 io.close(dir)
195 local obj = {}
196 local sub_path = {}
197 for i, v in ipairs(path) do sub_path[i] = v end
198 table.insert(sub_path, file_key)
199 install_autoloader(obj, category, sub_path)
200 rawset(self, key, obj)
201 try_exec(path_string .. "/__init.lua")
202 if merge then try_exec(merge_path_string .. "/__init.lua") end
203 return true
204 else
205 return false
206 end
207 end
208 if merge and try_exec(merge_path_string .. ".lua") then
209 elseif merge and try_dir(merge_path_string .. "/") then
210 elseif try_exec(path_string .. ".lua") then
211 elseif try_dir(path_string .. "/") then
212 else end
213 return rawget(self, key)
214 end
215 install_autoloader(_G, nil, {})
216 try_exec("../env/__init.lua")
217 end
219 -- interactive console mode
220 if not cgi then
221 local config_name = request.get_config_name()
222 if config_name then
223 execute.config(config_name)
224 end
225 return
226 end
228 local success, error_info = xpcall(
229 function()
231 -- execute configuration file
232 do
233 local config_name = request.get_config_name()
234 if config_name then
235 execute.config(config_name)
236 end
237 end
239 -- restore slots if coming from http redirect
240 if cgi.params.tempstore then
241 trace.restore_slots{}
242 local blob = tempstore.pop(cgi.params.tempstore)
243 if blob then slot.restore_all(blob) end
244 end
246 local function file_exists(filename)
247 local file = io.open(filename, "r")
248 if file then
249 io.close(file)
250 return true
251 else
252 return false
253 end
254 end
256 if cgi.params["_webmcp_404"] then
257 request.force_absolute_baseurl()
258 request.set_status("404 Not Found")
259 if request.get_404_route() then
260 request.forward(request.get_404_route())
261 else
262 error("No 404 page set.")
263 end
264 elseif request.get_action() then
265 trace.request{
266 module = request.get_module(),
267 action = request.get_action()
268 }
269 if
270 request.get_404_route() and
271 not file_exists(
272 encode.action_file_path{
273 module = request.get_module(),
274 action = request.get_action()
275 }
276 )
277 then
278 request.set_status("404 Not Found")
279 request.forward(request.get_404_route())
280 else
281 if cgi.method ~= "POST" then
282 request.set_status("405 Method Not Allowed")
283 cgi.add_header("Allow: POST")
284 error("Tried to invoke an action with a GET request.")
285 end
286 local action_status = execute.filtered_action{
287 module = request.get_module(),
288 action = request.get_action(),
289 }
290 if not request.is_rerouted() then
291 local routing_mode, routing_module, routing_view
292 routing_mode = cgi.params["_webmcp_routing." .. action_status .. ".mode"]
293 routing_module = cgi.params["_webmcp_routing." .. action_status .. ".module"]
294 routing_view = cgi.params["_webmcp_routing." .. action_status .. ".view"]
295 if not (routing_mode or routing_module or routing_view) then
296 action_status = "default"
297 routing_mode = cgi.params["_webmcp_routing.default.mode"]
298 routing_module = cgi.params["_webmcp_routing.default.module"]
299 routing_view = cgi.params["_webmcp_routing.default.view"]
300 end
301 assert(routing_module, "Routing information has no module.")
302 assert(routing_view, "Routing information has no view.")
303 if routing_mode == "redirect" then
304 local routing_params = {}
305 for key, value in pairs(cgi.params) do
306 local status, stripped_key = string.match(
307 key, "^_webmcp_routing%.([^%.]*)%.params%.(.*)$"
308 )
309 if status == action_status then
310 routing_params[stripped_key] = value
311 end
312 end
313 request.redirect{
314 module = routing_module,
315 view = routing_view,
316 id = cgi.params["_webmcp_routing." .. action_status .. ".id"],
317 params = routing_params
318 }
319 elseif routing_mode == "forward" then
320 request.forward{ module = routing_module, view = routing_view }
321 else
322 error("Missing or unknown routing mode in request parameters.")
323 end
324 end
325 end
326 else
327 -- no action
328 trace.request{
329 module = request.get_module(),
330 view = request.get_view()
331 }
332 if
333 request.get_404_route() and
334 not file_exists(
335 encode.view_file_path{
336 module = request.get_module(),
337 view = request.get_view()
338 }
339 )
340 then
341 request.set_status("404 Not Found")
342 request.forward(request.get_404_route())
343 end
344 end
346 if not request.get_redirect_data() then
347 request.process_forward()
348 execute.filtered_view{
349 module = request.get_module(),
350 view = request.get_view(),
351 }
352 end
354 -- force error due to missing absolute base URL until its too late to display error message
355 --if request.get_redirect_data() then
356 -- request.get_absolute_baseurl()
357 --end
359 end,
361 function(errobj)
362 return {
363 errobj = errobj,
364 stacktrace = string.gsub(
365 debug.traceback('', 2),
366 "^\r?\n?stack traceback:\r?\n?", ""
367 )
368 }
369 end
370 )
372 if not success then trace.error{} end
374 -- laufzeitermittlung
375 trace.exectime{ real = os.monotonic_hires_time(), cpu = os.clock() }
377 slot.select('trace', trace.render) -- render trace information
379 local redirect_data = request.get_redirect_data()
381 -- log error and switch to error layout, unless success
382 if not success then
383 local errobj = error_info.errobj
384 local stacktrace = error_info.stacktrace
385 if not request.get_status() then
386 request.set_status("500 Internal Server Error")
387 end
388 slot.set_layout('system_error')
389 slot.select('system_error', function()
390 if getmetatable(errobj) == mondelefant.errorobject_metatable then
391 slot.put(
392 "<p>Database error of class <b>",
393 encode.html(errobj.code),
394 "</b> occured:<br/><b>",
395 encode.html(errobj.message),
396 "</b></p>"
397 )
398 else
399 slot.put("<p><b>", encode.html(tostring(errobj)), "</b></p>")
400 end
401 slot.put("<p>Stack trace follows:<br/>")
402 slot.put(encode.html_newlines(encode.html(stacktrace)))
403 slot.put("</p>")
404 end)
405 elseif redirect_data then
406 local redirect_params = {}
407 for key, value in pairs(redirect_data.params) do
408 redirect_params[key] = value
409 end
410 local slot_dump = slot.dump_all()
411 if slot_dump ~= "" then
412 redirect_params.tempstore = tempstore.save(slot_dump)
413 end
414 cgi.redirect(
415 encode.url{
416 base = request.get_absolute_baseurl(),
417 module = redirect_data.module,
418 view = redirect_data.view,
419 id = redirect_data.id,
420 params = redirect_params
421 }
422 )
423 cgi.send_data()
424 end
426 if not success or not redirect_data then
428 local http_status = request.get_status()
429 if http_status then
430 cgi.set_status(http_status)
431 end
433 -- ajax
434 if request.is_ajax() then
435 cgi.set_content_type('text/html')
436 for i, slot_ident in ipairs{'main', 'actions', 'title', 'topnav', 'sidenav', 'debug', 'notice', 'warning', 'error'} do
437 local html = slot.get_content(slot_ident)
438 if html then
439 cgi.send_data("document.getElementById('" .. slot_ident .. "').innerHTML=" .. encode.json(html or '&nbsp;') .. ";")
440 end
441 end
442 -- oder ganz herkoemmlich
443 else
444 cgi.set_content_type(slot.get_content_type())
445 cgi.send_data(slot.render_layout())
446 end
447 end
449 exit()

Impressum / About Us