webmcp
changeset 0:9fdfb27f8e67 v1.0.0
Version 1.0.0
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/LICENSE Sun Oct 25 12:00:00 2009 +0100 1.3 @@ -0,0 +1,19 @@ 1.4 +Copyright (c) 2009 Public Software Group e. V., Berlin 1.5 + 1.6 +Permission is hereby granted, free of charge, to any person obtaining a 1.7 +copy of this software and associated documentation files (the "Software"), 1.8 +to deal in the Software without restriction, including without limitation 1.9 +the rights to use, copy, modify, merge, publish, distribute, sublicense, 1.10 +and/or sell copies of the Software, and to permit persons to whom the 1.11 +Software is furnished to do so, subject to the following conditions: 1.12 + 1.13 +The above copyright notice and this permission notice shall be included in 1.14 +all copies or substantial portions of the Software. 1.15 + 1.16 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1.17 +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1.18 +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 1.19 +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 1.20 +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 1.21 +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 1.22 +DEALINGS IN THE SOFTWARE.
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 2.2 +++ b/Makefile Sun Oct 25 12:00:00 2009 +0100 2.3 @@ -0,0 +1,53 @@ 2.4 +include Makefile.options 2.5 + 2.6 +all:: 2.7 + make documentation 2.8 + make accelerator 2.9 + make libraries 2.10 + make symlinks 2.11 + make precompile 2.12 + 2.13 +documentation:: 2.14 + rm -f doc/autodoc.tmp 2.15 + lua framework/bin/autodoc.lua framework/cgi-bin/ framework/env/ libraries/ > doc/autodoc.tmp 2.16 + cat doc/autodoc-header.htmlpart doc/autodoc.tmp doc/autodoc-footer.htmlpart > doc/autodoc.html 2.17 + rm -f doc/autodoc.tmp 2.18 + 2.19 +accelerator:: 2.20 + cd framework/accelerator; make 2.21 + 2.22 +libraries:: 2.23 + cd libraries/extos; make 2.24 + cd libraries/mondelefant; make 2.25 + cd libraries/multirand; make 2.26 + 2.27 +symlinks:: 2.28 + ln -s -f ../../libraries/atom/atom.lua framework/lib/ 2.29 + ln -s -f ../../libraries/extos/extos.so framework/lib/ 2.30 + ln -s -f ../../libraries/mondelefant/mondelefant.lua framework/lib/ 2.31 + ln -s -f ../../libraries/mondelefant/mondelefant_native.so framework/lib/ 2.32 + ln -s -f ../../libraries/mondelefant/mondelefant_atom_connector.lua framework/lib/ 2.33 + ln -s -f ../../libraries/multirand/multirand.so framework/lib/ 2.34 + ln -s -f ../../libraries/rocketcgi/rocketcgi.lua framework/lib/ 2.35 + ln -s -f ../../libraries/nihil/nihil.lua framework/lib/ 2.36 + ln -s -f ../../libraries/luatex/luatex.lua framework/lib/ 2.37 + 2.38 +precompile:: 2.39 + rm -Rf framework.precompiled 2.40 + rm -Rf demo-app.precompiled 2.41 + sh framework/bin/recursive-luac framework/ framework.precompiled/ 2.42 + rm -f framework.precompiled/accelerator/Makefile 2.43 + rm -f framework.precompiled/accelerator/webmcp_accelerator.c 2.44 + rm -f framework.precompiled/accelerator/webmcp_accelerator.o 2.45 + framework/bin/recursive-luac demo-app/ demo-app.precompiled/ 2.46 + 2.47 +clean:: 2.48 + rm -f doc/autodoc.tmp doc/autodoc.html 2.49 + rm -Rf framework.precompiled 2.50 + rm -Rf demo-app.precompiled 2.51 + rm -f demo-app/tmp/* 2.52 + rm -f framework/lib/* 2.53 + cd libraries/extos; make clean 2.54 + cd libraries/mondelefant; make clean 2.55 + cd libraries/multirand; make clean 2.56 + cd framework/accelerator; make clean
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 3.2 +++ b/Makefile.options Sun Oct 25 12:00:00 2009 +0100 3.3 @@ -0,0 +1,20 @@ 3.4 +# C compiler command 3.5 +CC = cc 3.6 + 3.7 +# linker command 3.8 +LD = ld 3.9 + 3.10 +# filename extension for shared libraries 3.11 +SLIB_EXT = so 3.12 + 3.13 +# C compiler flags 3.14 +CFLAGS = -O2 -Wall -I /usr/include -I /usr/local/include -I /usr/local/include/lua51 -I /usr/include/lua5.1 3.15 + 3.16 +# additional C compiler flags for parts which depend on PostgreSQL 3.17 +CFLAGS_PGSQL = -I /usr/include/postgresql -I /usr/include/postgresql/server -I /usr/local/include/postgresql -I /usr/local/include/postgresql/server 3.18 + 3.19 +# linker flags 3.20 +LDFLAGS = -shared -L /usr/lib -L /usr/local/lib -L /usr/local/lib/lua51 -L /usr/lib/lua5.1 3.21 + 3.22 +# additional linker flags for parts which depend on PostgreSQL 3.23 +LDFLAGS_PGSQL =
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 4.2 +++ b/demo-app/app/main/_filter/20_session.lua Sun Oct 25 12:00:00 2009 +0100 4.3 @@ -0,0 +1,19 @@ 4.4 +if cgi.cookies.session then 4.5 + app.session = Session:by_ident(cgi.cookies.session) 4.6 +end 4.7 +if not app.session then 4.8 + app.session = Session:new() 4.9 + cgi.add_header('Set-Cookie: session=' .. app.session.ident .. '; path=/' ) 4.10 +end 4.11 + 4.12 +request.set_csrf_secret(app.session.csrf_secret) 4.13 + 4.14 +if app.session.user then 4.15 + locale.set{ lang = app.session.user.lang or "en" } 4.16 +end 4.17 + 4.18 +if param.get("lang") then 4.19 + locale.set{ lang = param.get("lang") } 4.20 +end 4.21 + 4.22 +execute.inner()
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 5.2 +++ b/demo-app/app/main/_filter/21_auth.lua Sun Oct 25 12:00:00 2009 +0100 5.3 @@ -0,0 +1,25 @@ 5.4 +local auth_needed = not ( 5.5 + request.get_module() == 'index' 5.6 + and ( 5.7 + request.get_view() == 'login' 5.8 + or request.get_action() == 'login' 5.9 + ) 5.10 +) 5.11 + 5.12 +-- if not app.session.user_id then 5.13 +-- trace.debug("DEBUG: AUTHENTICATION BYPASS ENABLED") 5.14 +-- app.session.user_id = 1 5.15 +-- end 5.16 + 5.17 +if app.session.user == nil and auth_needed then 5.18 + trace.debug("Not authenticated yet.") 5.19 + request.redirect{ module = 'index', view = 'login' } 5.20 +else 5.21 + if auth_needed then 5.22 + trace.debug("Authentication accepted.") 5.23 + else 5.24 + trace.debug("No authentication needed.") 5.25 + end 5.26 + execute.inner() 5.27 + trace.debug("End of authentication filter.") 5.28 +end
6.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 6.2 +++ b/demo-app/app/main/_filter_action/23_write_priv.lua Sun Oct 25 12:00:00 2009 +0100 6.3 @@ -0,0 +1,7 @@ 6.4 +if 6.5 + not (request.get_module() == "index" and request.get_action() == "login") 6.6 + and not (request.get_module() == "index" and request.get_action() == "logout") 6.7 +then 6.8 + app.session.user:require_privilege("write") 6.9 +end 6.10 +execute.inner()
7.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 7.2 +++ b/demo-app/app/main/_filter_view/30_topnav.lua Sun Oct 25 12:00:00 2009 +0100 7.3 @@ -0,0 +1,83 @@ 7.4 +-- display navigation only, if user is logged in 7.5 +if app.session.user_id == nil then 7.6 + execute.inner() 7.7 + return 7.8 +end 7.9 + 7.10 +slot.select("topnav", function() 7.11 + ui.link{ 7.12 + attr = { class = "nav" }, 7.13 + text = _"Home", 7.14 + module = "index", 7.15 + view = "index" 7.16 + } 7.17 + ui.link{ 7.18 + attr = { class = "nav" }, 7.19 + text = _"Media", 7.20 + module = "medium" 7.21 + } 7.22 + ui.link{ 7.23 + attr = { class = "nav" }, 7.24 + text = _"Media types", 7.25 + module = "media_type" 7.26 + } 7.27 + ui.link{ 7.28 + attr = { class = "nav" }, 7.29 + text = _"Genres", 7.30 + module = "genre" 7.31 + } 7.32 + if app.session.user.admin then 7.33 + ui.link{ 7.34 + attr = { class = "nav" }, 7.35 + text = _"Users", 7.36 + module = "user" 7.37 + } 7.38 + end 7.39 + ui.container{ 7.40 + attr = { class = "nav lang_chooser" }, 7.41 + content = function() 7.42 + for i, lang in ipairs{"en", "de", "es"} do 7.43 + ui.container{ 7.44 + content = function() 7.45 + ui.link{ 7.46 + content = function() 7.47 + ui.image{ 7.48 + static = "lang/" .. lang .. ".png", 7.49 + attr = { alt = lang } 7.50 + } 7.51 + slot.put(lang) 7.52 + end, 7.53 + module = "index", 7.54 + action = "set_lang", 7.55 + params = { lang = lang }, 7.56 + routing = { 7.57 + default = { 7.58 + mode = "redirect", 7.59 + module = request.get_module(), 7.60 + view = request.get_view(), 7.61 + id = param.get_id_cgi(), 7.62 + params = param.get_all_cgi() 7.63 + } 7.64 + } 7.65 + } 7.66 + end 7.67 + } 7.68 + end 7.69 + end 7.70 + } 7.71 + 7.72 + ui.link{ 7.73 + attr = { class = "nav" }, 7.74 + text = _"Logout", 7.75 + module = "index", 7.76 + action = "logout", 7.77 + redirect_to = { 7.78 + ok = { 7.79 + module = "index", 7.80 + view = "login" 7.81 + } 7.82 + } 7.83 + } 7.84 +end) 7.85 + 7.86 +execute.inner()
8.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 8.2 +++ b/demo-app/app/main/_layout/default.html Sun Oct 25 12:00:00 2009 +0100 8.3 @@ -0,0 +1,58 @@ 8.4 +<html> 8.5 + <head> 8.6 + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> 8.7 + <title>WebMCP Demo Application</title> 8.8 + <link rel="stylesheet" type="text/css" media="screen" href="__BASEURL__/static/style.css" /> 8.9 + <link rel="stylesheet" type="text/css" media="screen" href="__BASEURL__/static/trace.css" /> 8.10 + </head> 8.11 + <body> 8.12 + <div class="layout_logo"> 8.13 + <div class="logo"> 8.14 + <img src="__BASEURL__/static/logo.png"> Demo 8.15 + </div> 8.16 + </div> 8.17 + <div class="layout_topnav"> 8.18 + <div class="topnav" id="topnav"> 8.19 + <!-- WEBMCP SLOT topnav --> 8.20 + </div> 8.21 + </div> 8.22 + <br style="clear: left;"> 8.23 + <div class="layout_sidenav"> 8.24 + <div class="sidenav" id="sidenav"> 8.25 + <!-- WEBMCP SLOT sidenav --> 8.26 + </div> 8.27 + </div> 8.28 + <div class="layout_content"> 8.29 + <div class="layout_title"> 8.30 + <!-- WEBMCP SLOT title --> 8.31 + </div> 8.32 + <br style="clear: left;"> 8.33 + <div class="layout_actions"> 8.34 + <!-- WEBMCP SLOT actions --> 8.35 + </div> 8.36 + <div class="layout_main"> 8.37 + <!-- WEBMCP SLOT main --> 8.38 + </div> 8.39 + <div class="layout_trace" id="layout_trace" style="xdisplay: none"> 8.40 + <div id="trace_show" onclick="document.getElementById('trace_content').style.display='block';this.style.display='none';">TRACE</div> 8.41 + <div id="trace_content" style="display: none;"> 8.42 + <!-- WEBMCP SLOT trace --> 8.43 + <div class="trace_close" onclick="document.getElementById('trace_show').style.display='block';document.getElementById('trace_content').style.display='none';"> 8.44 + close 8.45 + </div> 8.46 + </div> 8.47 + </div> 8.48 + </div> 8.49 + <div class="layout_notice" id="layout_notice" onclick="document.getElementById('layout_notice').style.display='none';"> 8.50 + <!-- WEBMCP SLOT notice --> 8.51 + </div> 8.52 + <div class="layout_warning" id="layout_warning" onclick="document.getElementById('layout_warning').style.display='none';"> 8.53 + <!-- WEBMCP SLOT warning --> 8.54 + </div> 8.55 + <div class="layout_error" id="layout_error" onclick="document.getElementById('layout_error').style.display='none';"> 8.56 + <!-- WEBMCP SLOT error --> 8.57 + </div> 8.58 + </body> 8.59 + <script> 8.60 + </script> 8.61 +</html> 8.62 \ No newline at end of file
9.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 9.2 +++ b/demo-app/app/main/_layout/system_error.html Sun Oct 25 12:00:00 2009 +0100 9.3 @@ -0,0 +1,41 @@ 9.4 +<html> 9.5 + <head> 9.6 + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> 9.7 + <title>webmcp demo application</title> 9.8 + <link rel="stylesheet" type="text/css" media="screen" href="__BASEURL__/static/style.css" /> 9.9 + <link rel="stylesheet" type="text/css" media="screen" href="__BASEURL__/static/trace.css" /> 9.10 + </head> 9.11 + <body class="system_error"> 9.12 + <div class="layout_content"> 9.13 + <div class="layout_title"> 9.14 + <div class="title"> 9.15 + <br /> 9.16 + <br /> 9.17 + System message 9.18 + </div> 9.19 + </div> 9.20 + <br style="clear: left;"> 9.21 + <div class="layout_actions"> 9.22 + 9.23 + </div> 9.24 + <div class="layout_main"> 9.25 + <div class="main"> 9.26 + <tt><!-- WEBMCP SLOT system_error --></tt> 9.27 + <br /> 9.28 + <br /> 9.29 + <br /> 9.30 + <br /> 9.31 + <button onclick="window.location.reload()">Retry request</button> 9.32 + <a href="__BASEURL__">index</a> 9.33 + </div> 9.34 + </div> 9.35 + </div> 9.36 + <div class="layout_trace" id="layout_trace" style="xdisplay: none"> 9.37 + <div id="trace_show" onclick="document.getElementById('trace_content').style.display='block';this.style.display='none';" style="display: none;">TRACE</div> 9.38 + <div id="trace_content"> 9.39 + <!-- WEBMCP SLOT trace --> 9.40 + <div class="trace_close" onclick="document.getElementById('trace_show').style.display='block';document.getElementById('trace_content').style.display='none';">close</div> 9.41 + </div> 9.42 + </div> 9.43 + </body> 9.44 +</html> 9.45 \ No newline at end of file
10.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 10.2 +++ b/demo-app/app/main/genre/_action/update.lua Sun Oct 25 12:00:00 2009 +0100 10.3 @@ -0,0 +1,24 @@ 10.4 +local genre 10.5 +local id = param.get_id() 10.6 +if id then 10.7 + genre = Genre:by_id(id) 10.8 +else 10.9 + genre = Genre:new() 10.10 +end 10.11 + 10.12 +if param.get("delete", atom.boolean) then 10.13 + local name = genre.name 10.14 + genre:destroy() 10.15 + slot.put_into("notice", _("Genre '#{name}' deleted", {name = name})) 10.16 + return 10.17 +end 10.18 + 10.19 +param.update(genre, "name", "description") 10.20 + 10.21 +genre:save() 10.22 + 10.23 +if id then 10.24 + slot.put_into("notice", _("Genre '#{name}' updated", {name = genre.name})) 10.25 +else 10.26 + slot.put_into("notice", _("Genre '#{name}' created", {name = genre.name})) 10.27 +end
11.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 11.2 +++ b/demo-app/app/main/genre/index.lua Sun Oct 25 12:00:00 2009 +0100 11.3 @@ -0,0 +1,46 @@ 11.4 +slot.put_into("title", encode.html(_"Genres")) 11.5 + 11.6 +slot.select("actions", function() 11.7 + if app.session.user.write_priv then 11.8 + ui.link{ 11.9 + content = _"Create new genre", 11.10 + module = "genre", 11.11 + view = "show" 11.12 + } 11.13 + end 11.14 +end) 11.15 + 11.16 + 11.17 +local selector = Genre:new_selector():add_order_by('"name", "id"') 11.18 + 11.19 +slot.select("main", function() 11.20 + ui.paginate{ 11.21 + selector = selector, 11.22 + content = function() 11.23 + ui.list{ 11.24 + records = selector:exec(), 11.25 + columns = { 11.26 + { 11.27 + field_attr = { style = "float: right;" }, 11.28 + label = _"Id", 11.29 + name = "id" 11.30 + }, 11.31 + { 11.32 + label = _"Name", 11.33 + name = "name" 11.34 + }, 11.35 + { 11.36 + content = function(record) 11.37 + ui.link{ 11.38 + content = _"Show", 11.39 + module = "genre", 11.40 + view = "show", 11.41 + id = record.id 11.42 + } 11.43 + end 11.44 + }, 11.45 + } 11.46 + } 11.47 + end 11.48 + } 11.49 +end)
12.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 12.2 +++ b/demo-app/app/main/genre/show.lua Sun Oct 25 12:00:00 2009 +0100 12.3 @@ -0,0 +1,63 @@ 12.4 +local genre 12.5 +local id = param.get_id() 12.6 +if id then 12.7 + genre = Genre:by_id(id) 12.8 +end 12.9 + 12.10 +if genre then 12.11 + slot.put_into("title", encode.html(_"Genre")) 12.12 +else 12.13 + slot.put_into("title", encode.html(_"New genre")) 12.14 +end 12.15 + 12.16 +slot.select("actions", function() 12.17 + ui.link{ 12.18 + content = _"Back", 12.19 + module = "genre" 12.20 + } 12.21 + if genre and app.session.user.write_priv then 12.22 + ui.link{ 12.23 + content = _"Delete", 12.24 + form_attr = { 12.25 + onsubmit = "return confirm('" .. _'Are you sure?' .. "');" 12.26 + }, 12.27 + module = "genre", 12.28 + action = "update", 12.29 + id = genre.id, 12.30 + params = { delete = true }, 12.31 + routing = { 12.32 + default = { 12.33 + mode = "redirect", 12.34 + module = "genre", 12.35 + view = "index" 12.36 + } 12.37 + } 12.38 + } 12.39 + end 12.40 +end) 12.41 + 12.42 +slot.select("main", function() 12.43 + ui.form{ 12.44 + attr = { class = "vertical" }, 12.45 + record = genre, 12.46 + readonly = not app.session.user.write_priv, 12.47 + module = "genre", 12.48 + action = "update", 12.49 + id = id, 12.50 + routing = { 12.51 + default = { 12.52 + mode = "redirect", 12.53 + module = "genre", 12.54 + view = "index" 12.55 + } 12.56 + }, 12.57 + content = function() 12.58 + if id then 12.59 + ui.field.integer{ label = _"Id", name = "id", readonly = true } 12.60 + end 12.61 + ui.field.text{ label = _"Name", name = "name" } 12.62 + ui.field.text{ label = _"Description", name = "description", multiline = true } 12.63 + ui.submit{ text = _"Save" } 12.64 + end 12.65 + } 12.66 +end)
13.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 13.2 +++ b/demo-app/app/main/index/_action/login.lua Sun Oct 25 12:00:00 2009 +0100 13.3 @@ -0,0 +1,12 @@ 13.4 +local user = User:by_ident_and_password(param.get('ident'), param.get('password')) 13.5 + 13.6 +if user then 13.7 + app.session.user = user 13.8 + app.session:save() 13.9 + slot.put_into('notice', _'Login successful!') 13.10 + trace.debug('User authenticated') 13.11 +else 13.12 + slot.put_into('error', _'Invalid username or password!') 13.13 + trace.debug('User NOT authenticated') 13.14 + return false 13.15 +end
14.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 14.2 +++ b/demo-app/app/main/index/_action/logout.lua Sun Oct 25 12:00:00 2009 +0100 14.3 @@ -0,0 +1,4 @@ 14.4 +if app.session then 14.5 + app.session:destroy() 14.6 + slot.put_into("notice", _"Logout successful") 14.7 +end
15.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 15.2 +++ b/demo-app/app/main/index/_action/set_lang.lua Sun Oct 25 12:00:00 2009 +0100 15.3 @@ -0,0 +1,6 @@ 15.4 +app.session.user.lang = param.get("lang") 15.5 +app.session.user:save() 15.6 + 15.7 +locale.set{ lang = app.session.user.lang } 15.8 + 15.9 +slot.put_into("notice", _"Language changed") 15.10 \ No newline at end of file
16.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 16.2 +++ b/demo-app/app/main/index/index.lua Sun Oct 25 12:00:00 2009 +0100 16.3 @@ -0,0 +1,3 @@ 16.4 +slot.put_into('title', encode.html(_"webmcp demo application")) 16.5 + 16.6 +slot.put_into('main', encode.html(_"Welcome to webmcp demo application"))
17.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 17.2 +++ b/demo-app/app/main/index/login.lua Sun Oct 25 12:00:00 2009 +0100 17.3 @@ -0,0 +1,47 @@ 17.4 +slot.put_into("title", encode.html(_"Password login")) 17.5 + 17.6 +slot.select("main", function() 17.7 + 17.8 + ui.form{ 17.9 + attr = { class = "vertical" }, 17.10 + module = "index", 17.11 + action = "login", 17.12 + routing = { 17.13 + default = { 17.14 + mode = "redirect", 17.15 + module = "index", 17.16 + view = "index" 17.17 + } 17.18 + }, 17.19 + content = function() 17.20 + 17.21 + ui.container{ 17.22 + attr = { class = "lang_chooser" }, 17.23 + content = function() 17.24 + for i, lang in ipairs{"en", "de", "es"} do 17.25 + ui.container{ 17.26 + content = function() 17.27 + ui.link{ 17.28 + content = function() 17.29 + ui.image{ 17.30 + static = "lang/" .. lang .. ".png", 17.31 + attr = { alt = lang } 17.32 + } 17.33 + slot.put(lang) 17.34 + end, 17.35 + module = "index", 17.36 + view = "login", 17.37 + params = { lang = lang } 17.38 + } 17.39 + end 17.40 + } 17.41 + end 17.42 + end 17.43 + } 17.44 + 17.45 + ui.field.text{ label = _"Username", name = "ident" } 17.46 + ui.field.text{ label = _"Password", name = "password" } 17.47 + ui.submit{ text = _"Login" } 17.48 + end 17.49 + } 17.50 +end) 17.51 \ No newline at end of file
18.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 18.2 +++ b/demo-app/app/main/media_type/_action/update.lua Sun Oct 25 12:00:00 2009 +0100 18.3 @@ -0,0 +1,24 @@ 18.4 +local media_type 18.5 +local id = param.get_id() 18.6 +if id then 18.7 + media_type = MediaType:by_id(id) 18.8 +else 18.9 + media_type = MediaType:new() 18.10 +end 18.11 + 18.12 +if param.get("delete", atom.boolean) then 18.13 + local name = media_type.name 18.14 + media_type:destroy() 18.15 + slot.put_into("notice", _("Media type '#{name}' deleted", {name = name})) 18.16 + return 18.17 +end 18.18 + 18.19 +param.update(media_type, "name", "description") 18.20 + 18.21 +media_type:save() 18.22 + 18.23 +if id then 18.24 + slot.put_into("notice", _("Media type '#{name}' updated", {name = media_type.name})) 18.25 +else 18.26 + slot.put_into("notice", _("Media type '#{name}' created", {name = media_type.name})) 18.27 +end
19.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 19.2 +++ b/demo-app/app/main/media_type/index.lua Sun Oct 25 12:00:00 2009 +0100 19.3 @@ -0,0 +1,46 @@ 19.4 +slot.put_into("title", encode.html(_"Media types")) 19.5 + 19.6 +slot.select("actions", function() 19.7 + if app.session.user.write_priv then 19.8 + ui.link{ 19.9 + content = _"Create new media type", 19.10 + module = "media_type", 19.11 + view = "show" 19.12 + } 19.13 + end 19.14 +end) 19.15 + 19.16 + 19.17 +local selector = MediaType:new_selector():add_order_by('"name", "id"') 19.18 + 19.19 +slot.select("main", function() 19.20 + ui.paginate{ 19.21 + selector = selector, 19.22 + content = function() 19.23 + ui.list{ 19.24 + records = selector:exec(), 19.25 + columns = { 19.26 + { 19.27 + field_attr = { style = "float: right;" }, 19.28 + label = _"Id", 19.29 + name = "id" 19.30 + }, 19.31 + { 19.32 + label = _"Name", 19.33 + name = "name" 19.34 + }, 19.35 + { 19.36 + content = function(record) 19.37 + ui.link{ 19.38 + content = _"Show", 19.39 + module = "media_type", 19.40 + view = "show", 19.41 + id = record.id 19.42 + } 19.43 + end 19.44 + }, 19.45 + } 19.46 + } 19.47 + end 19.48 + } 19.49 +end)
20.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 20.2 +++ b/demo-app/app/main/media_type/show.lua Sun Oct 25 12:00:00 2009 +0100 20.3 @@ -0,0 +1,63 @@ 20.4 +local media_type 20.5 +local id = param.get_id() 20.6 +if id then 20.7 + media_type = MediaType:by_id(id) 20.8 +end 20.9 + 20.10 +if media_type then 20.11 + slot.put_into("title", encode.html(_"Media type")) 20.12 +else 20.13 + slot.put_into("title", encode.html(_"New media type")) 20.14 +end 20.15 + 20.16 +slot.select("actions", function() 20.17 + ui.link{ 20.18 + content = _"Back", 20.19 + module = "media_type" 20.20 + } 20.21 + if media_type and app.session.user.write_priv then 20.22 + ui.link{ 20.23 + content = _"Delete", 20.24 + form_attr = { 20.25 + onsubmit = "return confirm('" .. _'Are you sure?' .. "');" 20.26 + }, 20.27 + module = "media_type", 20.28 + action = "update", 20.29 + id = media_type.id, 20.30 + params = { delete = true }, 20.31 + routing = { 20.32 + default = { 20.33 + mode = "redirect", 20.34 + module = "media_type", 20.35 + view = "index" 20.36 + } 20.37 + } 20.38 + } 20.39 + end 20.40 +end) 20.41 + 20.42 +slot.select("main", function() 20.43 + ui.form{ 20.44 + attr = { class = "vertical" }, 20.45 + record = media_type, 20.46 + readonly = not app.session.user.write_priv, 20.47 + module = "media_type", 20.48 + action = "update", 20.49 + id = id, 20.50 + routing = { 20.51 + default = { 20.52 + mode = "redirect", 20.53 + module = "media_type", 20.54 + view = "index" 20.55 + } 20.56 + }, 20.57 + content = function() 20.58 + if id then 20.59 + ui.field.integer{ label = _"Id", name = "id", readonly = true } 20.60 + end 20.61 + ui.field.text{ label = _"Name", name = "name" } 20.62 + ui.field.text{ label = _"Description", name = "description", multiline = true } 20.63 + ui.submit{ text = _"Save" } 20.64 + end 20.65 + } 20.66 +end)
21.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 21.2 +++ b/demo-app/app/main/medium/_action/update.lua Sun Oct 25 12:00:00 2009 +0100 21.3 @@ -0,0 +1,51 @@ 21.4 +local medium 21.5 +local id = param.get_id() 21.6 +if id then 21.7 + medium = Medium:by_id(id) 21.8 +else 21.9 + medium = Medium:new() 21.10 +end 21.11 + 21.12 +if param.get("delete", atom.boolean) then 21.13 + local name = medium.name 21.14 + medium:destroy() 21.15 + slot.put_into("notice", _("Medium '#{name}' deleted", {name = name})) 21.16 + return 21.17 +end 21.18 + 21.19 +param.update(medium, "media_type_id", "name", "copyprotected") 21.20 + 21.21 +medium:save() 21.22 + 21.23 +param.update_relationship{ 21.24 + param_name = "genres", 21.25 + id = medium.id, 21.26 + connecting_model = Classification, 21.27 + own_reference = "medium_id", 21.28 + foreign_reference = "genre_id" 21.29 +} 21.30 + 21.31 +for index, prefix in param.iterate("tracks") do 21.32 + local id = param.get(prefix .. "id", atom.integer) 21.33 + local track 21.34 + if id then 21.35 + track = Track:by_id(id) 21.36 + elseif #param.get(prefix .. "name") > 0 then 21.37 + track = Track:new() 21.38 + track.medium_id = medium.id 21.39 + else 21.40 + break 21.41 + end 21.42 + track.position = param.get(prefix .. "position", atom.integer) 21.43 + track.name = param.get(prefix .. "name") 21.44 + track.description = param.get(prefix .. "description") 21.45 + track.duration = param.get(prefix .. "duration") 21.46 + track:save() 21.47 +end 21.48 + 21.49 + 21.50 +if id then 21.51 + slot.put_into("notice", _("Medium '#{name}' updated", {name = medium.name})) 21.52 +else 21.53 + slot.put_into("notice", _("Medium '#{name}' created", {name = medium.name})) 21.54 +end
22.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 22.2 +++ b/demo-app/app/main/medium/index.lua Sun Oct 25 12:00:00 2009 +0100 22.3 @@ -0,0 +1,50 @@ 22.4 +slot.put_into("title", encode.html(_"Media")) 22.5 + 22.6 +slot.select("actions", function() 22.7 + if app.session.user.write_priv then 22.8 + ui.link{ 22.9 + content = _"Create new medium", 22.10 + module = "medium", 22.11 + view = "show" 22.12 + } 22.13 + end 22.14 +end) 22.15 + 22.16 + 22.17 +local selector = Medium:new_selector():add_order_by('"name", "id"') 22.18 + 22.19 +slot.select("main", function() 22.20 + ui.paginate{ 22.21 + selector = selector, 22.22 + content = function() 22.23 + ui.list{ 22.24 + records = selector:exec(), 22.25 + columns = { 22.26 + { 22.27 + field_attr = { style = "float: right;" }, 22.28 + label = _"Id", 22.29 + name = "id" 22.30 + }, 22.31 + { 22.32 + label = _"Name", 22.33 + name = "name" 22.34 + }, 22.35 + { 22.36 + label = _"Copy protected", 22.37 + name = "copyprotected" 22.38 + }, 22.39 + { 22.40 + content = function(record) 22.41 + ui.link{ 22.42 + content = _"Show", 22.43 + module = "medium", 22.44 + view = "show", 22.45 + id = record.id 22.46 + } 22.47 + end 22.48 + }, 22.49 + } 22.50 + } 22.51 + end 22.52 + } 22.53 +end) 22.54 \ No newline at end of file
23.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 23.2 +++ b/demo-app/app/main/medium/show.lua Sun Oct 25 12:00:00 2009 +0100 23.3 @@ -0,0 +1,117 @@ 23.4 +local medium 23.5 +local id = param.get_id() 23.6 +if id then 23.7 + medium = Medium:by_id(id) 23.8 +end 23.9 + 23.10 +if medium then 23.11 + slot.put_into("title", encode.html(_"Medium")) 23.12 +else 23.13 + slot.put_into("title", encode.html(_"New medium")) 23.14 +end 23.15 + 23.16 +slot.select("actions", function() 23.17 + ui.link{ 23.18 + content = _"Back", 23.19 + module = "medium" 23.20 + } 23.21 + if medium and app.session.user.write_priv then 23.22 + ui.link{ 23.23 + content = _"Delete", 23.24 + form_attr = { 23.25 + onsubmit = "return confirm(" .. encode.json(_'Are you sure?') .. ");" 23.26 + }, 23.27 + module = "medium", 23.28 + action = "update", 23.29 + id = medium.id, 23.30 + params = { delete = true }, 23.31 + routing = { 23.32 + default = { 23.33 + mode = "redirect", 23.34 + module = "medium", 23.35 + view = "index" 23.36 + } 23.37 + } 23.38 + } 23.39 + end 23.40 +end) 23.41 + 23.42 +slot.select("main", function() 23.43 + ui.form{ 23.44 + attr = { class = "vertical" }, 23.45 + record = medium, 23.46 + readonly = not app.session.user.write_priv, 23.47 + module = "medium", 23.48 + action = "update", 23.49 + id = id, 23.50 + routing = { 23.51 + default = { 23.52 + mode = "redirect", 23.53 + module = "medium", 23.54 + view = "index" 23.55 + } 23.56 + }, 23.57 + content = function() 23.58 + if id then 23.59 + ui.field.integer{ label = _"Id", name = "id", readonly = true } 23.60 + end 23.61 + ui.field.select{ 23.62 + label = _"Media type", 23.63 + name = "media_type_id", 23.64 + foreign_records = MediaType:new_selector():exec(), 23.65 + foreign_id = "id", 23.66 + foreign_name = "name" 23.67 + } 23.68 + ui.field.text{ label = _"Name", name = "name" } 23.69 + ui.field.boolean{ label = _"Copy protected", name = "copyprotected" } 23.70 + 23.71 + ui.multiselect{ 23.72 + name = "genres[]", 23.73 + label = _"Genres", 23.74 + style = "select", 23.75 + attr = { size = 5 }, 23.76 + foreign_records = Genre:new_selector():exec(), 23.77 + connecting_records = medium and medium.classifications or {}, 23.78 + own_id = "id", 23.79 + own_reference = "medium_id", 23.80 + foreign_reference = "genre_id", 23.81 + foreign_id = "id", 23.82 + foreign_name = "name", 23.83 + } 23.84 + local tracks = medium and medium.tracks or {} 23.85 + for i = 1, 5 do 23.86 + tracks[#tracks+1] = Track:new() 23.87 + end 23.88 + ui.list{ 23.89 + label = _"Tracks", 23.90 + prefix = "tracks", 23.91 + records = tracks, 23.92 + columns = { 23.93 + { 23.94 + label = _"Pos", 23.95 + name = "position", 23.96 + }, 23.97 + { 23.98 + label = _"Name", 23.99 + name = "name", 23.100 + }, 23.101 + { 23.102 + label = _"Description", 23.103 + name = "description", 23.104 + }, 23.105 + { 23.106 + label = _"Duration", 23.107 + name = "duration", 23.108 + }, 23.109 + { 23.110 + content = function() 23.111 + ui.field.hidden{ name = "id" } 23.112 + end 23.113 + } 23.114 + } 23.115 + } 23.116 + 23.117 + ui.submit{ text = _"Save" } 23.118 + end 23.119 + } 23.120 +end)
24.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 24.2 +++ b/demo-app/app/main/user/_action/update.lua Sun Oct 25 12:00:00 2009 +0100 24.3 @@ -0,0 +1,24 @@ 24.4 +local user 24.5 +local id = param.get_id() 24.6 +if id then 24.7 + user = User:by_id(id) 24.8 +else 24.9 + user = User:new() 24.10 +end 24.11 + 24.12 +if param.get("delete", atom.boolean) then 24.13 + local name = user.name 24.14 + user:destroy() 24.15 + slot.put_into("notice", _("User '#{name}' deleted", {name = name})) 24.16 + return 24.17 +end 24.18 + 24.19 +param.update(user, "ident", "password", "name", "write_priv", "admin") 24.20 + 24.21 +user:save() 24.22 + 24.23 +if id then 24.24 + slot.put_into("notice", _("User '#{name}' updated", {name = user.name})) 24.25 +else 24.26 + slot.put_into("notice", _("User '#{name}' created", {name = user.name})) 24.27 +end
25.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 25.2 +++ b/demo-app/app/main/user/_filter/25_require_admin.lua Sun Oct 25 12:00:00 2009 +0100 25.3 @@ -0,0 +1,3 @@ 25.4 +app.session.user:require_privilege("admin") 25.5 + 25.6 +execute.inner()
26.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 26.2 +++ b/demo-app/app/main/user/index.lua Sun Oct 25 12:00:00 2009 +0100 26.3 @@ -0,0 +1,56 @@ 26.4 +slot.put_into("title", encode.html(_"Users")) 26.5 + 26.6 +slot.select("actions", function() 26.7 + ui.link{ 26.8 + content = _"Create new user", 26.9 + module = "user", 26.10 + view = "show" 26.11 + } 26.12 +end) 26.13 + 26.14 + 26.15 +local selector = User:new_selector():add_order_by('"ident", "id"') 26.16 + 26.17 +slot.select("main", function() 26.18 + ui.paginate{ 26.19 + selector = selector, 26.20 + content = function() 26.21 + ui.list{ 26.22 + records = selector:exec(), 26.23 + columns = { 26.24 + { 26.25 + field_attr = { style = "float: right;" }, 26.26 + label = _"Id", 26.27 + name = "id" 26.28 + }, 26.29 + { 26.30 + label = _"Ident", 26.31 + name = "ident" 26.32 + }, 26.33 + { 26.34 + label = _"Name", 26.35 + name = "name" 26.36 + }, 26.37 + { 26.38 + label = _"w", 26.39 + name = "write_priv" 26.40 + }, 26.41 + { 26.42 + label = _"Admin", 26.43 + name = "admin" 26.44 + }, 26.45 + { 26.46 + content = function(record) 26.47 + ui.link{ 26.48 + content = _"Show", 26.49 + module = "user", 26.50 + view = "show", 26.51 + id = record.id 26.52 + } 26.53 + end 26.54 + }, 26.55 + } 26.56 + } 26.57 + end 26.58 + } 26.59 +end) 26.60 \ No newline at end of file
27.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 27.2 +++ b/demo-app/app/main/user/show.lua Sun Oct 25 12:00:00 2009 +0100 27.3 @@ -0,0 +1,65 @@ 27.4 +local user 27.5 +local id = param.get_id() 27.6 +if id then 27.7 + user = User:by_id(id) 27.8 +end 27.9 + 27.10 +if user then 27.11 + slot.put_into("title", encode.html(_"User")) 27.12 +else 27.13 + slot.put_into("title", encode.html(_"New user")) 27.14 +end 27.15 + 27.16 +slot.select("actions", function() 27.17 + ui.link{ 27.18 + content = _"Back", 27.19 + module = "user" 27.20 + } 27.21 + if user then 27.22 + ui.link{ 27.23 + content = _"Delete", 27.24 + form_attr = { 27.25 + onsubmit = "return confirm('" .. _'Are you sure?' .. "');" 27.26 + }, 27.27 + module = "user", 27.28 + action = "update", 27.29 + id = user.id, 27.30 + params = { delete = true }, 27.31 + routing = { 27.32 + default = { 27.33 + mode = "redirect", 27.34 + module = "user", 27.35 + view = "index" 27.36 + } 27.37 + } 27.38 + } 27.39 + end 27.40 +end) 27.41 + 27.42 +slot.select("main", function() 27.43 + ui.form{ 27.44 + attr = { class = "vertical" }, 27.45 + record = user, 27.46 + module = "user", 27.47 + action = "update", 27.48 + id = id, 27.49 + routing = { 27.50 + default = { 27.51 + mode = "redirect", 27.52 + module = "user", 27.53 + view = "index" 27.54 + } 27.55 + }, 27.56 + content = function() 27.57 + if id then 27.58 + ui.field.integer{ label = _"Id", name = "id", readonly = true } 27.59 + end 27.60 + ui.field.text{ label = _"Ident", name = "ident" } 27.61 + ui.field.text{ label = _"Password", name = "password" } 27.62 + ui.field.text{ label = _"Name", name = "name" } 27.63 + ui.field.boolean{ label = _"Write Priv", name = "write_priv" } 27.64 + ui.field.boolean{ label = _"Admin", name = "admin" } 27.65 + ui.submit{ text = _"Save" } 27.66 + end 27.67 + } 27.68 +end)
28.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 28.2 +++ b/demo-app/config/demo.lua Sun Oct 25 12:00:00 2009 +0100 28.3 @@ -0,0 +1,47 @@ 28.4 +-- uncomment the following two lines to use C implementations of chosen 28.5 +-- functions and to disable garbage collection during the request, to 28.6 +-- increase speed: 28.7 +-- 28.8 +-- require 'webmcp_accelerator' 28.9 +-- collectgarbage("stop") 28.10 + 28.11 +-- open and set default database handle 28.12 +db = assert(mondelefant.connect{ 28.13 + engine='postgresql', 28.14 + dbname='webmcp_demo' 28.15 +}) 28.16 +at_exit(function() 28.17 + db:close() 28.18 +end) 28.19 +function mondelefant.class_prototype:get_db_conn() return db end 28.20 + 28.21 +-- enable output of SQL commands in trace system 28.22 +function db:sql_tracer(command) 28.23 + return function(error_info) 28.24 + local error_info = error_info or {} 28.25 + trace.sql{ command = command, error_position = error_info.position } 28.26 + end 28.27 +end 28.28 + 28.29 +-- 'request.get_relative_baseurl()' should be replaced by the absolute 28.30 +-- base URL of the application, as otherwise HTTP redirects will not be 28.31 +-- standard compliant 28.32 +request.set_absolute_baseurl(request.get_relative_baseurl()) 28.33 + 28.34 +-- uncomment the following lines, if you want to use a database driven 28.35 +-- tempstore (for flash messages): 28.36 +-- 28.37 +-- function tempstore.save(blob) 28.38 +-- return Tempstore:create(blob) 28.39 +-- end 28.40 +-- function tempstore.pop(key) 28.41 +-- return Tempstore:data_by_key(key) 28.42 +-- end 28.43 + 28.44 + 28.45 +function mondelefant.class_prototype:by_id(id) 28.46 + return self:new_selector() 28.47 + :add_where{ "id = ?", id } 28.48 + :optional_object_mode() 28.49 + :exec() 28.50 +end
29.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 29.2 +++ b/demo-app/db/schema.sql Sun Oct 25 12:00:00 2009 +0100 29.3 @@ -0,0 +1,79 @@ 29.4 +-- only needed for database driven tempstore (see application config) 29.5 +CREATE TABLE "tempstore" ( 29.6 + "key" TEXT PRIMARY KEY, 29.7 + "data" BYTEA NOT NULL ); 29.8 + 29.9 +-- Attention: USER is a reserved word in PostgreSQL. We use it anyway in 29.10 +-- this example. Don't forget the double quotes where neccessary. 29.11 +CREATE TABLE "user" ( 29.12 + "id" SERIAL8 PRIMARY KEY, 29.13 + "ident" TEXT NOT NULL, 29.14 + "password" TEXT, 29.15 + "name" TEXT, 29.16 + "lang" TEXT, 29.17 + "write_priv" BOOLEAN NOT NULL DEFAULT FALSE, 29.18 + "admin" BOOLEAN NOT NULL DEFAULT FALSE ); 29.19 + 29.20 +CREATE TABLE "session" ( 29.21 + "ident" TEXT PRIMARY KEY, 29.22 + "csrf_secret" TEXT NOT NULL, 29.23 + "expiry" TIMESTAMPTZ NOT NULL DEFAULT NOW() + '24 hours', 29.24 + "user_id" INT8 REFERENCES "user" ("id") ON DELETE SET NULL ON UPDATE CASCADE ); 29.25 + 29.26 +CREATE TABLE "media_type" ( 29.27 + "id" SERIAL8 PRIMARY KEY, 29.28 + "name" TEXT NOT NULL, 29.29 + "description" TEXT ); 29.30 + 29.31 +CREATE TABLE "genre" ( 29.32 + "id" SERIAL8 PRIMARY KEY, 29.33 + "name" TEXT NOT NULL, 29.34 + "description" TEXT ); 29.35 + 29.36 +CREATE TABLE "medium" ( 29.37 + "id" SERIAL8 PRIMARY KEY, 29.38 + "media_type_id" INT8 NOT NULL REFERENCES "media_type" ("id") ON DELETE RESTRICT ON UPDATE CASCADE, 29.39 + "name" TEXT NOT NULL, 29.40 + "copyprotected" BOOLEAN NOT NULL ); 29.41 + 29.42 +CREATE TABLE "classification" ( 29.43 + PRIMARY KEY ("medium_id", "genre_id"), 29.44 + "medium_id" INT8 REFERENCES "medium" ("id") ON DELETE CASCADE ON UPDATE CASCADE, 29.45 + "genre_id" INT8 REFERENCES "genre" ("id") ON DELETE CASCADE ON UPDATE CASCADE ); 29.46 + 29.47 +CREATE TABLE "track" ( 29.48 + "id" SERIAL8 PRIMARY KEY, 29.49 + "medium_id" INT8 NOT NULL REFERENCES "medium" ("id") ON DELETE CASCADE ON UPDATE CASCADE, 29.50 + "position" INT8 NOT NULL, 29.51 + "name" TEXT NOT NULL, 29.52 + "description" TEXT, 29.53 + "duration" INTERVAL, 29.54 + UNIQUE ("medium_id", "position") ); 29.55 + 29.56 +INSERT INTO "user" ("ident", "password", "name", "write_priv", "admin") 29.57 + VALUES ('admin', 'admin', 'Administrator', true, true); 29.58 + 29.59 +INSERT INTO "user" ("ident", "password", "name", "write_priv", "admin") 29.60 + VALUES ('user', 'User', 'User', true, false); 29.61 + 29.62 +INSERT INTO "user" ("ident", "password", "name", "write_priv", "admin") 29.63 + VALUES ('anon', 'anon', 'Anonymous', false, false); 29.64 + 29.65 +INSERT INTO "media_type" ("name", "description") VALUES ('CD', ''); 29.66 +INSERT INTO "media_type" ("name", "description") VALUES ('Tape', ''); 29.67 + 29.68 +INSERT INTO "genre" ("name", "description") VALUES ('Klassik', ''); 29.69 +INSERT INTO "genre" ("name", "description") VALUES ('Gospel', ''); 29.70 +INSERT INTO "genre" ("name", "description") VALUES ('Jazz', ''); 29.71 +INSERT INTO "genre" ("name", "description") VALUES ('Traditional', ''); 29.72 +INSERT INTO "genre" ("name", "description") VALUES ('Latin', ''); 29.73 +INSERT INTO "genre" ("name", "description") VALUES ('Blues', ''); 29.74 +INSERT INTO "genre" ("name", "description") VALUES ('Rhythm & blues', ''); 29.75 +INSERT INTO "genre" ("name", "description") VALUES ('Funk', ''); 29.76 +INSERT INTO "genre" ("name", "description") VALUES ('Rock', ''); 29.77 +INSERT INTO "genre" ("name", "description") VALUES ('Pop', ''); 29.78 +INSERT INTO "genre" ("name", "description") VALUES ('Country', ''); 29.79 +INSERT INTO "genre" ("name", "description") VALUES ('Electronic', ''); 29.80 +INSERT INTO "genre" ("name", "description") VALUES ('Ska / Reggea', ''); 29.81 +INSERT INTO "genre" ("name", "description") VALUES ('Hip hop / Rap', ''); 29.82 +
30.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 30.2 +++ b/demo-app/locale/translations.de.lua Sun Oct 25 12:00:00 2009 +0100 30.3 @@ -0,0 +1,59 @@ 30.4 +#!/usr/bin/env lua 30.5 +return { 30.6 +["Admin"] = "Admin"; 30.7 +["Are you sure?"] = "Bist Du sicher?"; 30.8 +["Back"] = "Zurück"; 30.9 +["Copy protected"] = "Kopiergeschützt"; 30.10 +["Create new genre"] = "Neues Genre anlegen"; 30.11 +["Create new media type"] = "Neuen Medientyp anlegen"; 30.12 +["Create new medium"] = "Neues Medium anlegen"; 30.13 +["Create new user"] = "Neuen Benutzer anlegen"; 30.14 +["Delete"] = "Löschen"; 30.15 +["Description"] = "Beschreibung"; 30.16 +["Duration"] = "Dauer"; 30.17 +["Genre"] = "Genre"; 30.18 +["Genre '#{name}' created"] = "Genre '#{name}' angelegt"; 30.19 +["Genre '#{name}' deleted"] = "Genre '#{name}' gelöscht"; 30.20 +["Genre '#{name}' updated"] = "Genre '#{name}' aktualisiert"; 30.21 +["Genres"] = "Genres"; 30.22 +["Home"] = "Startseite"; 30.23 +["Id"] = "Id"; 30.24 +["Ident"] = "Ident"; 30.25 +["Invalid username or password!"] = "Ungülter Benutzername oder Kennwort"; 30.26 +["Language changed"] = "Sprache gewechselt"; 30.27 +["Login"] = "Anmeldung"; 30.28 +["Login successful!"] = "Anmeldung erfolgreich!"; 30.29 +["Logout"] = "Abmelden"; 30.30 +["Logout successful"] = "Anmeldung erfolgreich"; 30.31 +["Media"] = "Medium"; 30.32 +["Media type"] = "Medientyp"; 30.33 +["Media type '#{name}' created"] = "Medientyp '#{name}' angelegt"; 30.34 +["Media type '#{name}' deleted"] = "Medientyp '#{name}' gelöscht"; 30.35 +["Media type '#{name}' updated"] = "Medientyp '#{name}' aktualisiert"; 30.36 +["Media types"] = "Medientypen"; 30.37 +["Medium"] = "Medium"; 30.38 +["Medium '#{name}' created"] = "Medium '#{name}' angelegt"; 30.39 +["Medium '#{name}' deleted"] = "Medium '#{name}' gelöscht"; 30.40 +["Medium '#{name}' updated"] = "Medium '#{name}' aktualisiert"; 30.41 +["Name"] = "Name"; 30.42 +["New genre"] = "Neues Genre"; 30.43 +["New media type"] = "Neuer Medientyp"; 30.44 +["New medium"] = "Neues Medium"; 30.45 +["New user"] = "Neuer Benutzer"; 30.46 +["Password"] = "Kennwort"; 30.47 +["Password login"] = "Anmeldung mit Kennwort"; 30.48 +["Pos"] = "Pos"; 30.49 +["Save"] = "Speichern"; 30.50 +["Show"] = "Anzeigen"; 30.51 +["Tracks"] = "Stücke"; 30.52 +["User"] = "Benutzer"; 30.53 +["User '#{name}' created"] = "Benutzer '#{name}' angelegt"; 30.54 +["User '#{name}' deleted"] = "Benutzer '#{name}' gelöscht"; 30.55 +["User '#{name}' updated"] = "Benutzer '#{name}' aktualisiert"; 30.56 +["Username"] = "Benutzername"; 30.57 +["Users"] = "Benutzer"; 30.58 +["Welcome to webmcp demo application"] = "Willkommen zur webmcp Demo-Anwendung"; 30.59 +["Write Priv"] = "Schreibrecht"; 30.60 +["w"] = "w"; 30.61 +["webmcp demo application"] = "webmcp Demo-Anwendung"; 30.62 +}
31.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 31.2 +++ b/demo-app/locale/translations.en.lua Sun Oct 25 12:00:00 2009 +0100 31.3 @@ -0,0 +1,59 @@ 31.4 +#!/usr/bin/env lua 31.5 +return { 31.6 +["Admin"] = false; 31.7 +["Are you sure?"] = false; 31.8 +["Back"] = false; 31.9 +["Copy protected"] = false; 31.10 +["Create new genre"] = false; 31.11 +["Create new media type"] = false; 31.12 +["Create new medium"] = false; 31.13 +["Create new user"] = false; 31.14 +["Delete"] = false; 31.15 +["Description"] = false; 31.16 +["Duration"] = false; 31.17 +["Genre"] = false; 31.18 +["Genre '#{name}' created"] = false; 31.19 +["Genre '#{name}' deleted"] = false; 31.20 +["Genre '#{name}' updated"] = false; 31.21 +["Genres"] = false; 31.22 +["Home"] = false; 31.23 +["Id"] = false; 31.24 +["Ident"] = false; 31.25 +["Invalid username or password!"] = false; 31.26 +["Language changed"] = false; 31.27 +["Login"] = false; 31.28 +["Login successful!"] = false; 31.29 +["Logout"] = false; 31.30 +["Logout successful"] = false; 31.31 +["Media"] = false; 31.32 +["Media type"] = false; 31.33 +["Media type '#{name}' created"] = false; 31.34 +["Media type '#{name}' deleted"] = false; 31.35 +["Media type '#{name}' updated"] = false; 31.36 +["Media types"] = false; 31.37 +["Medium"] = false; 31.38 +["Medium '#{name}' created"] = false; 31.39 +["Medium '#{name}' deleted"] = false; 31.40 +["Medium '#{name}' updated"] = false; 31.41 +["Name"] = false; 31.42 +["New genre"] = false; 31.43 +["New media type"] = false; 31.44 +["New medium"] = false; 31.45 +["New user"] = false; 31.46 +["Password"] = false; 31.47 +["Password login"] = false; 31.48 +["Pos"] = false; 31.49 +["Save"] = false; 31.50 +["Show"] = false; 31.51 +["Tracks"] = false; 31.52 +["User"] = false; 31.53 +["User '#{name}' created"] = false; 31.54 +["User '#{name}' deleted"] = false; 31.55 +["User '#{name}' updated"] = false; 31.56 +["Username"] = false; 31.57 +["Users"] = false; 31.58 +["Welcome to webmcp demo application"] = false; 31.59 +["Write Priv"] = false; 31.60 +["w"] = false; 31.61 +["webmcp demo application"] = false; 31.62 +}
32.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 32.2 +++ b/demo-app/locale/translations.es.lua Sun Oct 25 12:00:00 2009 +0100 32.3 @@ -0,0 +1,59 @@ 32.4 +#!/usr/bin/env lua 32.5 +return { 32.6 +["Admin"] = "Admin"; 32.7 +["Are you sure?"] = "Estás seguro?"; 32.8 +["Back"] = "Atrás"; 32.9 +["Copy protected"] = "Protegido anticopia"; 32.10 +["Create new genre"] = "Crear un nuevo género"; 32.11 +["Create new media type"] = "Crear un nuevo soporte"; 32.12 +["Create new medium"] = "Crear un nuevo medio"; 32.13 +["Create new user"] = "Crear un nuevo usuario"; 32.14 +["Delete"] = "Borrar"; 32.15 +["Description"] = "Descripción"; 32.16 +["Duration"] = "Duración"; 32.17 +["Genre"] = "Género"; 32.18 +["Genre '#{name}' created"] = "Género '#{name}' creado"; 32.19 +["Genre '#{name}' deleted"] = "Género '#{name}' borrado"; 32.20 +["Genre '#{name}' updated"] = "Género '#{name}' actualizado"; 32.21 +["Genres"] = "Géneros"; 32.22 +["Home"] = "Inicio"; 32.23 +["Id"] = "Id"; 32.24 +["Ident"] = "Ident"; 32.25 +["Invalid username or password!"] = "Nombre de usuario o Contraseña Incorrecta"; 32.26 +["Language changed"] = "Idioma cambiado"; 32.27 +["Login"] = "Login"; 32.28 +["Login successful!"] = "Login realizado!"; 32.29 +["Logout"] = "Logout"; 32.30 +["Logout successful"] = "Logout realizado"; 32.31 +["Media"] = "Medios"; 32.32 +["Media type"] = "Soporte"; 32.33 +["Media type '#{name}' created"] = "Soporte '#{name}' creado"; 32.34 +["Media type '#{name}' deleted"] = "Soporte '#{name}' borrado"; 32.35 +["Media type '#{name}' updated"] = "Soporte '#{name}' actualizado"; 32.36 +["Media types"] = "Soporte"; 32.37 +["Medium"] = "Medio"; 32.38 +["Medium '#{name}' created"] = "Medio '#{name}' creado"; 32.39 +["Medium '#{name}' deleted"] = "Medio '#{name}' borrado"; 32.40 +["Medium '#{name}' updated"] = "Medio '#{name}' actualizado"; 32.41 +["Name"] = "Nombre"; 32.42 +["New genre"] = "Nuevo género"; 32.43 +["New media type"] = "Nuevo soporte"; 32.44 +["New medium"] = "Nuevo medio"; 32.45 +["New user"] = "Nuevo usuario"; 32.46 +["Password"] = "Contraseña"; 32.47 +["Password login"] = "Login con contraseña"; 32.48 +["Pos"] = "Pos"; 32.49 +["Save"] = "Guardar"; 32.50 +["Show"] = "Mostrar"; 32.51 +["Tracks"] = "Pistas"; 32.52 +["User"] = "Usuario"; 32.53 +["User '#{name}' created"] = "Usuario '#{name}' creado"; 32.54 +["User '#{name}' deleted"] = "Usuario '#{name}' borrado"; 32.55 +["User '#{name}' updated"] = "Usuario '#{name}' actualizado"; 32.56 +["Username"] = "Nombre de usuario"; 32.57 +["Users"] = "Usuarios"; 32.58 +["Welcome to webmcp demo application"] = "Bienvenido a la aplicación de demostración de webmcp"; 32.59 +["Write Priv"] = "Permiso de escritura"; 32.60 +["w"] = "w"; 32.61 +["webmcp demo application"] = "Aplicación de demostración de webmcp"; 32.62 +}
33.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 33.2 +++ b/demo-app/model/classification.lua Sun Oct 25 12:00:00 2009 +0100 33.3 @@ -0,0 +1,22 @@ 33.4 +Classification = mondelefant.new_class() 33.5 +Classification.table = 'classification' 33.6 + 33.7 +Classification:add_reference{ 33.8 + mode = 'm1', -- many (m) Classifications can refer to one (1) Medium 33.9 + to = "Medium", -- name of referenced model (quoting avoids auto-loading of model here) 33.10 + this_key = 'medium_id', -- our key in the classification table 33.11 + that_key = 'id', -- other key in the medium table 33.12 + ref = 'medium', -- name of reference 33.13 + back_ref = nil, -- not used for m1 relation! 33.14 + default_order = nil -- not used for m1 relation! 33.15 +} 33.16 + 33.17 +Classification:add_reference{ 33.18 + mode = 'm1', -- many (m) Classifications can refer to one (1) Medium 33.19 + to = "Genre", -- name of referenced model (quoting avoids auto-loading of model here) 33.20 + this_key = 'genre_id', -- our key in the classification table 33.21 + that_key = 'id', -- other key in the genre table 33.22 + ref = 'genre', -- name of reference 33.23 + back_ref = nil, -- not used for m1 relation! 33.24 + default_order = nil -- not used for m1 relation! 33.25 +}
34.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 34.2 +++ b/demo-app/model/genre.lua Sun Oct 25 12:00:00 2009 +0100 34.3 @@ -0,0 +1,25 @@ 34.4 +Genre = mondelefant.new_class() 34.5 +Genre.table = 'genre' 34.6 + 34.7 +Genre:add_reference{ 34.8 + mode = '1m', -- one (1) Genre is used for many (m) Classifications 34.9 + to = "Classification", -- name of referenced model (using a string instead of reference avoids auto-loading here) 34.10 + this_key = 'id', -- own key in genre table 34.11 + that_key = 'genre_id', -- other key in classification table 34.12 + ref = 'classifications', -- name of reference 34.13 + back_ref = 'genre', -- each autoloaded Classification automatically refers back to the Genre 34.14 + default_order = '"media_id"' -- order Classifications by SQL expression "media_id" 34.15 +} 34.16 + 34.17 +Genre:add_reference{ 34.18 + mode = 'mm', -- many (m) Genres belong to many (m) Medium entries 34.19 + to = "Medium", -- name of referenced model (quoting avoids auto-loading here) 34.20 + this_key = 'id', -- (primary) key of genre table 34.21 + that_key = 'id', -- (primary) key of medium talbe 34.22 + connected_by_table = 'classification', -- table connecting genres with media 34.23 + connected_by_this_key = 'genre_id', -- key in connection table referencing genres 34.24 + connected_by_that_key = 'medium_id', -- key in connection table referencing media 34.25 + ref = 'media', -- name of reference 34.26 + back_ref = nil, -- not used for mm relation! 34.27 + default_order = '"medium"."name", "medium"."id"' -- mm references need qualified names in SQL order expression! 34.28 +}
35.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 35.2 +++ b/demo-app/model/media_type.lua Sun Oct 25 12:00:00 2009 +0100 35.3 @@ -0,0 +1,12 @@ 35.4 +MediaType = mondelefant.new_class() 35.5 +MediaType.table = 'media_type' 35.6 + 35.7 +MediaType:add_reference{ 35.8 + mode = '1m', -- one (1) MediaType is set for many (m) media 35.9 + to = "Medium", -- name of referenced model (quoting avoids auto-loading here) 35.10 + this_key = 'id', -- own key in media_type table 35.11 + that_key = 'media_type_id', -- other key in medium table 35.12 + ref = 'media', -- name of reference 35.13 + back_ref = 'media_type', -- each autoloaded Medium automatically refers back to the MediaType 35.14 + default_order = '"name", "id"' -- order media by SQL expression "name", "id" 35.15 +}
36.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 36.2 +++ b/demo-app/model/medium.lua Sun Oct 25 12:00:00 2009 +0100 36.3 @@ -0,0 +1,45 @@ 36.4 +Medium = mondelefant.new_class() 36.5 +Medium.table = 'medium' 36.6 + 36.7 +Medium:add_reference{ 36.8 + mode = 'm1', -- many (m) Medium entries can refer to one (1) MediaType 36.9 + to = "MediaType", -- name of referenced model (quoting avoids auto-loading here) 36.10 + this_key = 'media_type_id', -- own key in medium table 36.11 + that_key = 'id', -- other key in media_type table 36.12 + ref = 'media_type', -- name of reference 36.13 + back_ref = nil, -- not used for m1 relation! 36.14 + default_order = nil -- not used for m1 relation! 36.15 +} 36.16 + 36.17 +Medium:add_reference{ 36.18 + mode = '1m', -- one (1) Medium has many (m) Classifications 36.19 + to = "Classification", -- name of referenced model (quoting avoids auto-loading here) 36.20 + this_key = 'id', -- own key in medium table 36.21 + that_key = 'medium_id', -- other key in classification table 36.22 + ref = 'classifications', -- name of reference 36.23 + back_ref = 'medium', -- each autoloaded classification automatically refers back to the Medium 36.24 + default_order = '"genre_id"' -- order classifications by SQL expression "genre_id" 36.25 +} 36.26 + 36.27 +Medium:add_reference{ 36.28 + mode = 'mm', -- many (m) Media belong to many (m) Genres 36.29 + to = "Genre", -- name of referenced model (quoting avoids auto-loading here) 36.30 + this_key = 'id', -- (primary) key of medium table 36.31 + that_key = 'id', -- (primary) key of genre talbe 36.32 + connected_by_table = 'classification', -- table connecting media with genres 36.33 + connected_by_this_key = 'medium_id', -- key in classification table referencing media 36.34 + connected_by_that_key = 'genre_id', -- key in classification table referencing genres 36.35 + ref = 'genres', -- name of reference 36.36 + back_ref = nil, -- not used for mm relation! 36.37 + default_order = '"genre"."name", "genre"."id"' -- mm references need qualified names in SQL order expression! 36.38 +} 36.39 + 36.40 +Medium:add_reference{ 36.41 + mode = '1m', -- one (1) Medium has many (m) Tracks 36.42 + to = "Track", -- name of referenced model (quoting avoids auto-loading here) 36.43 + this_key = 'id', -- own key in medium table 36.44 + that_key = 'medium_id', -- other key in track table 36.45 + ref = 'tracks', -- name of reference 36.46 + back_ref = 'medium', -- each autoloaded classification automatically refers back to the Medium 36.47 + default_order = '"position"' -- order tracks by SQL expression "position" 36.48 +}
37.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 37.2 +++ b/demo-app/model/session.lua Sun Oct 25 12:00:00 2009 +0100 37.3 @@ -0,0 +1,35 @@ 37.4 +Session = mondelefant.new_class() 37.5 +Session.table = 'session' 37.6 +Session.primary_key = { "ident" } 37.7 + 37.8 +Session:add_reference{ 37.9 + mode = 'm1', -- many (m) sessions refer to one (1) user 37.10 + to = "User", -- name of referenced model (quoting avoids auto-loading here) 37.11 + this_key = 'user_id', -- own key in session table 37.12 + that_key = 'id', -- other key in user table 37.13 + ref = 'user', -- name of reference 37.14 + back_ref = nil, -- not used for m1 relation! 37.15 + default_order = nil -- not used for m1 relation! 37.16 +} 37.17 + 37.18 +local function random_string() 37.19 + return multirand.string( 37.20 + 32, 37.21 + '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' 37.22 + ) 37.23 +end 37.24 + 37.25 +function Session:new() 37.26 + local session = self.prototype.new(self) -- super call 37.27 + session.ident = random_string() 37.28 + session.csrf_secret = random_string() 37.29 + session:save() 37.30 + return session 37.31 +end 37.32 + 37.33 +function Session:by_ident(ident) 37.34 + local selector = self:new_selector() 37.35 + selector:add_where{ 'ident = ?', ident } 37.36 + selector:optional_object_mode() 37.37 + return selector:exec() 37.38 +end
38.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 38.2 +++ b/demo-app/model/tempstore.lua Sun Oct 25 12:00:00 2009 +0100 38.3 @@ -0,0 +1,25 @@ 38.4 +Tempstore = mondelefant.new_class() 38.5 +Tempstore.table = 'tempstore' 38.6 + 38.7 +function Tempstore:by_key(key) 38.8 + local selector = self:new_selector() 38.9 + selector:add_where{ 'key = ?', key } 38.10 + selector:optional_object_mode() 38.11 + return selector:exec() 38.12 +end 38.13 + 38.14 +function Tempstore:data_by_key(key) 38.15 + local tempstore = Tempstore:by_key(key) 38.16 + if tempstore then 38.17 + tempstore:destroy() 38.18 + return tempstore.data 38.19 + end 38.20 +end 38.21 + 38.22 +function Tempstore:create(data) 38.23 + tempstore = Tempstore:new() 38.24 + tempstore.key = multirand.string(22, '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz') 38.25 + tempstore.data = data 38.26 + tempstore:save() 38.27 + return tempstore.key 38.28 +end
39.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 39.2 +++ b/demo-app/model/track.lua Sun Oct 25 12:00:00 2009 +0100 39.3 @@ -0,0 +1,12 @@ 39.4 +Track = mondelefant.new_class() 39.5 +Track.table = 'track' 39.6 + 39.7 +Track:add_reference{ 39.8 + mode = 'm1', -- many (m) Tracks can refer to one (1) Medium 39.9 + to = "Medium", -- name of referenced model (quoting avoids auto-loading of model here) 39.10 + this_key = 'medium_id', -- our key in the track table 39.11 + that_key = 'id', -- other key in the medium table 39.12 + ref = 'medium', -- name of reference 39.13 + back_ref = nil, -- not used for m1 relation! 39.14 + default_order = nil -- not used for m1 relation! 39.15 +}
40.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 40.2 +++ b/demo-app/model/user.lua Sun Oct 25 12:00:00 2009 +0100 40.3 @@ -0,0 +1,33 @@ 40.4 +User = mondelefant.new_class() 40.5 +User.table = 'user' 40.6 + 40.7 +User:add_reference{ 40.8 + mode = '1m', -- one (1) user can have many (m) sessions 40.9 + to = "Session", -- referenced model (quoting avoids auto-loading here) 40.10 + this_key = 'id', -- own key in user table 40.11 + that_key = 'user_id', -- other key in session table 40.12 + ref = 'sessions', -- name of reference 40.13 + back_ref = 'user', -- each autoloaded Session automatically refers back to the User 40.14 + default_order = '"ident"' -- order sessions by SQL expression "ident" 40.15 +} 40.16 + 40.17 +function User:by_ident_and_password(ident, password) 40.18 + local selector = self:new_selector() 40.19 + selector:add_where{ 'ident = ? AND password = ?', ident, password } 40.20 + selector:optional_object_mode() 40.21 + return selector:exec() 40.22 +end 40.23 + 40.24 +function User.object_get:name_with_login() 40.25 + return self.name .. ' (' .. self.login .. ')' 40.26 +end 40.27 + 40.28 +function User.object:require_privilege(privilege) 40.29 + if privilege == "admin" then 40.30 + assert(self.admin, "Administrator privilege required") 40.31 + elseif privilege == "write" then 40.32 + assert(self.write_priv, "Write privilege required") 40.33 + else 40.34 + error("Unknown privilege passed to require_privilege method of User") 40.35 + end 40.36 +end
41.1 Binary file demo-app/static/lang/de.png has changed
42.1 Binary file demo-app/static/lang/en.png has changed
43.1 Binary file demo-app/static/lang/es.png has changed
44.1 Binary file demo-app/static/logo.png has changed
45.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 45.2 +++ b/demo-app/static/style.css Sun Oct 25 12:00:00 2009 +0100 45.3 @@ -0,0 +1,383 @@ 45.4 +/* 45.5 + * ********** body *********** 45.6 + */ 45.7 + 45.8 +body { 45.9 + background-color: #ddd; 45.10 + font-family: sans-serif; 45.11 + margin: 0; 45.12 + padding: 0; 45.13 + font-size: 10pt; 45.14 +} 45.15 + 45.16 +body, td, th, input, select { 45.17 + font-size: 10pt; 45.18 +} 45.19 + 45.20 +div { 45.21 + padding: 0px; 45.22 + margin: 0px; 45.23 +} 45.24 + 45.25 +/* 45.26 + * ********** Logo *********** 45.27 + */ 45.28 + 45.29 +.layout_logo { 45.30 + background: #fff; 45.31 + border: 1px solid #444; 45.32 + border-left: none; 45.33 + border-top: none; 45.34 + color: #444; 45.35 + float: left; 45.36 + height: 2.29em; 45.37 + margin-bottom: 2ex; 45.38 + padding-bottom: 0.3ex; 45.39 + padding-left: 0.3ex; 45.40 + padding-top: 0.3ex; 45.41 +} 45.42 + 45.43 +.logo { 45.44 + font-size: 1em; 45.45 + font-weight: bold; 45.46 +} 45.47 + 45.48 +.logo img { 45.49 + height: 2.29em; 45.50 + margin-bottom: -0.7em; 45.51 +} 45.52 + 45.53 +/* 45.54 + * ********** Top navigation *********** 45.55 + */ 45.56 + 45.57 +.layout_topnav { 45.58 + float: left; 45.59 + margin-bottom: 2em; 45.60 + margin-left: 2ex; 45.61 +} 45.62 + 45.63 +.slot_topnav .nav { 45.64 + background: #fff; 45.65 + border: 1px solid #444; 45.66 + border-top: none; 45.67 + color: #000; 45.68 + display: block; 45.69 + float: left; 45.70 + font-weight: bold; 45.71 + height: 2.29em; 45.72 + margin-right: 1ex; 45.73 + padding-bottom: 0.3ex; 45.74 + padding-left: 0.3ex; 45.75 + padding-right: 1ex; 45.76 + padding-top: 0.3ex; 45.77 + text-decoration: none; 45.78 +} 45.79 + 45.80 + 45.81 + 45.82 +.vertical .lang_chooser { 45.83 + margin-left: 25ex; 45.84 + margin-bottom: 1ex; 45.85 +} 45.86 + 45.87 +.vertical .lang_chooser div { 45.88 + margin-right: 1ex; 45.89 +} 45.90 + 45.91 +.lang_chooser div { 45.92 + float: left; 45.93 + text-align: center; 45.94 +} 45.95 + 45.96 +.lang_chooser a { 45.97 + color: #000; 45.98 + font-size: 75%; 45.99 +} 45.100 + 45.101 +.lang_chooser img { 45.102 + display: block; 45.103 +} 45.104 + 45.105 +.lang_chooser img { 45.106 + border: 1px solid white; 45.107 +} 45.108 + 45.109 +.lang_chooser a:hover img { 45.110 + border: 1px solid #444; 45.111 +} 45.112 + 45.113 +.lang_chooser a:hover { 45.114 + background: #444; 45.115 + color: #fff; 45.116 +} 45.117 + 45.118 + 45.119 +.slot_topnav a:hover { 45.120 + background: #444; 45.121 + color: #fff; 45.122 +} 45.123 + 45.124 +/* 45.125 + * ********** Content *********** 45.126 + */ 45.127 + 45.128 +.layout_content { 45.129 + margin-left: 2ex; 45.130 + margin-right: 2ex; 45.131 +} 45.132 + 45.133 + 45.134 +/* 45.135 + * ********** Messages *********** 45.136 + */ 45.137 + 45.138 +.layout_notice, .layout_error, .layout_warning { 45.139 + background: #fff; 45.140 + font-weight: bold; 45.141 + right: 2ex; 45.142 + line-height: 1.7em; 45.143 + position: absolute; 45.144 + top: 2ex; 45.145 + width: 60ex; 45.146 + -moz-opacity:0.7; 45.147 +} 45.148 + 45.149 +.slot_notice, .slot_warning, .slot_error { 45.150 + padding: 2ex; 45.151 +} 45.152 + 45.153 +.slot_notice { 45.154 + color: green; 45.155 + border: 2px solid green; 45.156 +} 45.157 + 45.158 +.slot_warning { 45.159 + color: orange; 45.160 + border: 2px solid orange; 45.161 +} 45.162 + 45.163 +.slot_error { 45.164 + color: red; 45.165 + border: 2px solid red; 45.166 +} 45.167 + 45.168 + 45.169 + 45.170 +/* 45.171 + * ********** Title, Actions and Main *********** 45.172 + */ 45.173 + 45.174 +.layout_title { 45.175 + background: #fff; 45.176 + border: 1px solid #444; 45.177 + border-bottom: none; 45.178 + padding: 0.5ex; 45.179 + font-size: 120%; 45.180 + font-weight: bold; 45.181 + float: left; 45.182 +} 45.183 + 45.184 +.layout_title .object_class { 45.185 + float: left; 45.186 + font-size: 0.7em; 45.187 + font-weight: normal; 45.188 + margin-right: 1ex; 45.189 +} 45.190 + 45.191 +.layout_actions { 45.192 + background: #444; 45.193 + border-left: 1px solid #444; 45.194 + padding-top: 0.5ex; 45.195 + padding-bottom: 0.5ex; 45.196 +} 45.197 + 45.198 +.slot_actions a { 45.199 + color: #fff; 45.200 + text-decoration: none; 45.201 + padding: 0.5ex; 45.202 +} 45.203 + 45.204 +.slot_actions a:hover { 45.205 + background: #ccc; 45.206 + color: #444; 45.207 +} 45.208 + 45.209 +.slot_main { 45.210 + background: #fff; 45.211 + border: 1px solid #444; 45.212 + border-top: none; 45.213 + padding: 2ex; 45.214 + padding-bottom: 0px; 45.215 +} 45.216 + 45.217 + 45.218 + 45.219 +/* 45.220 + * ********** ui *********** 45.221 + */ 45.222 + 45.223 + 45.224 +form.ui_link { 45.225 + display: inline; 45.226 + margin: 0px; 45.227 + padding: 0px; 45.228 +} 45.229 + 45.230 +.vertical { 45.231 + margin-bottom: 1em; 45.232 +} 45.233 + 45.234 +.vertical:after, .vertical div:after { 45.235 + clear: left; 45.236 + content: "."; 45.237 + display: block; 45.238 + height: 0; 45.239 + visibility: hidden; 45.240 +} 45.241 + 45.242 +.vertical label { 45.243 + float: left; 45.244 + width: 25ex; 45.245 + padding-top: 0.7em; 45.246 + font-size: 80%; 45.247 + font-weight: bold; 45.248 + color: #666; 45.249 + text-align: right; 45.250 + text-transform: uppercase; 45.251 +} 45.252 + 45.253 +.vertical select, 45.254 +.vertical input, 45.255 +.vertical textarea, 45.256 +.vertical span { 45.257 + margin-left: 1em; 45.258 + margin-bottom: 2ex; 45.259 +} 45.260 + 45.261 +.vertical textarea { 45.262 + border: none; 45.263 + border: 1px dotted #777; 45.264 + padding: 0px; 45.265 +} 45.266 + 45.267 +.vertical input, 45.268 +.vertical select { 45.269 + border: 1px dotted #777; 45.270 +} 45.271 + 45.272 +.vertical textarea { 45.273 + width: 80ex; 45.274 + height: 5em; 45.275 + font-family: sans-serif; 45.276 + font-size: 100%; 45.277 + padding: 0.5ex; 45.278 +} 45.279 + 45.280 +input[type=text] { 45.281 + background: #fff; 45.282 + padding: 0.5ex; 45.283 +} 45.284 + 45.285 +input[type=submit] { 45.286 + margin-left: 25ex; 45.287 + border: 1px dotted #777; 45.288 +} 45.289 + 45.290 +input[type=submit]:hover { 45.291 + border: 1px solid #777; 45.292 +} 45.293 + 45.294 +/* ui.list */ 45.295 + 45.296 +table.ui_list { 45.297 + border-collapse: collapse; 45.298 +} 45.299 + 45.300 +ul.ui_list { 45.301 + padding: 0px; 45.302 + margin: 0px; 45.303 +} 45.304 + 45.305 +.ui_list li { 45.306 + clear: left; 45.307 + list-style-type: none; 45.308 +} 45.309 + 45.310 +.ui_list_title { 45.311 + font-size: 110%; 45.312 + font-weight: bold; 45.313 + margin-bottom: 0.2em; 45.314 +} 45.315 + 45.316 + 45.317 +li.ui_list_head { 45.318 + font-weight: bold; 45.319 + background: #777; 45.320 + color: #fff; 45.321 +} 45.322 + 45.323 +.ui_list li:hover, 45.324 +.ui_list tr:hover { 45.325 + background: #ddd; 45.326 +} 45.327 + 45.328 +.ui_list li.ui_list_head:hover, 45.329 +.ui_list tr.ui_list_head:hover { 45.330 + background: #777; 45.331 +} 45.332 + 45.333 +.ui_list a { 45.334 + text-decoration: none; 45.335 + color: #000; 45.336 + border-bottom: 1px dashed #777; 45.337 +} 45.338 + 45.339 +.ui_list a:hover { 45.340 + border-bottom: 1px solid #000; 45.341 +} 45.342 + 45.343 +.ui_list li div { 45.344 + float: left; 45.345 +} 45.346 + 45.347 +.ui_list td { 45.348 + vertical-align: top; 45.349 +} 45.350 + 45.351 +.ui_list li div, 45.352 +.ui_list td { 45.353 + padding-top: 0.4ex; 45.354 + padding-bottom: 0.4ex; 45.355 + padding-right: 0.4em; 45.356 +} 45.357 + 45.358 +.ui_list li div div { 45.359 + padding: 0px; 45.360 +} 45.361 + 45.362 +.ui_list li div a { 45.363 + text-decoration: none; 45.364 + color: #000; 45.365 +} 45.366 + 45.367 +.ui_list_col_align_right { 45.368 + text-align: right; 45.369 +} 45.370 + 45.371 +.ui_paginate_select { 45.372 + text-align: center; 45.373 +} 45.374 + 45.375 +.ui_paginate_select a { 45.376 + padding-left: 1ex; 45.377 + padding-right: 1ex; 45.378 + background-color: #ccc; 45.379 + color: #000; 45.380 +} 45.381 + 45.382 +.ui_paginate_select a.active, 45.383 +.ui_paginate_select a:hover { 45.384 + background-color: #444; 45.385 + color: #fff; 45.386 +}
46.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 46.2 +++ b/demo-app/static/trace.css Sun Oct 25 12:00:00 2009 +0100 46.3 @@ -0,0 +1,153 @@ 46.4 +.layout_trace { 46.5 + position: absolute; 46.6 + right: 0; 46.7 + margin-top: 20px; 46.8 + border: 1px solid #404040; 46.9 + font-size: 70%; 46.10 + padding: 5px; 46.11 + background: #ffe0c0; 46.12 +} 46.13 + 46.14 +#trace_show { 46.15 + cursor: pointer; 46.16 +} 46.17 + 46.18 +.trace_list { 46.19 + margin: 0px; 46.20 + margin-bottom: 10px; 46.21 + padding: 0px; 46.22 + list-style-type: none; 46.23 +} 46.24 + 46.25 +.trace_list .trace_list { 46.26 + border-top: 1px solid; 46.27 + margin-bottom: 0px; 46.28 +} 46.29 + 46.30 +.trace_list li { 46.31 + margin: 3px; 46.32 + padding: 0px; 46.33 +} 46.34 + 46.35 +.trace_head { 46.36 + font-weight: bold; 46.37 + margin: 1px; 46.38 +} 46.39 + 46.40 +.trace_error { 46.41 + border: 3px solid red; 46.42 + background-color: black; 46.43 + color: #c00000; 46.44 + text-align: center; 46.45 + text-decoration: blink; 46.46 +} 46.47 + 46.48 +.trace_error_position { 46.49 + color: red; 46.50 + text-decoration: blink; 46.51 + font-weight: bold; 46.52 +} 46.53 + 46.54 +.trace_config { 46.55 + border: 1px solid #608000; 46.56 + background-color: #ffffff; 46.57 + color: #608000; 46.58 +} 46.59 + 46.60 +.trace_config .trace_list { 46.61 + border-color: #608000; 46.62 +} 46.63 + 46.64 +.trace_request { 46.65 + border: 1px solid #6000ff; 46.66 + background-color: #c080ff; 46.67 + color: #6000ff; 46.68 +} 46.69 + 46.70 +.trace_request .trace_list { 46.71 + border-color: #6000ff; 46.72 +} 46.73 + 46.74 +.trace_filter { 46.75 + border: 1px solid #606060; 46.76 + background-color: #c0c0c0; 46.77 + color: #606060; 46.78 +} 46.79 + 46.80 +.trace_filter .trace_list { 46.81 + border-color: #606060; 46.82 +} 46.83 + 46.84 +.trace_view { 46.85 + border: 1px solid #0000ff; 46.86 + background-color: #40c0ff; 46.87 + color: #0000ff; 46.88 +} 46.89 + 46.90 +.trace_view .trace_list { 46.91 + border-color: #0000ff; 46.92 +} 46.93 + 46.94 +.trace_action_success { 46.95 + border: 1px solid #006000; 46.96 + background-color: #80ff80; 46.97 + color: #006000; 46.98 +} 46.99 + 46.100 +.trace_action_success .trace_list { 46.101 + border-color: #006000; 46.102 +} 46.103 + 46.104 +.trace_action_softfail { 46.105 + border: 1px solid #600000; 46.106 + background-color: #ff6020; 46.107 + color: #600000; 46.108 +} 46.109 + 46.110 +.trace_action_softfail .trace_list { 46.111 + border-color: #600000; 46.112 +} 46.113 + 46.114 +.trace_action_status { 46.115 + font-weight: bold; 46.116 +} 46.117 + 46.118 +.trace_action_neutral { 46.119 + border: 1px solid #600000; 46.120 + background-color: #ffc040; 46.121 + color: #600000; 46.122 +} 46.123 + 46.124 +.trace_action_neutral .trace_list { 46.125 + border-color: #600000; 46.126 +} 46.127 + 46.128 +.trace_redirect, .trace_forward { 46.129 + border: 1px solid #404000; 46.130 + background-color: #c08040; 46.131 + color: #404000; 46.132 +} 46.133 + 46.134 +.trace_redirect .trace_list, .trace_forward .trace_list { 46.135 + border-color: #404000; 46.136 +} 46.137 + 46.138 +.trace_exectime { 46.139 + border: 1px solid black; 46.140 + background-color: #404040; 46.141 + color: white; 46.142 +} 46.143 + 46.144 +.trace_exectime .trace_list { 46.145 + border-color: white; 46.146 +} 46.147 + 46.148 +.trace_close { 46.149 + border: 1px solid black; 46.150 + background-color: #605040; 46.151 + margin: 3px; 46.152 + padding: 3px; 46.153 + color: white; 46.154 + text-align: center; 46.155 + cursor: pointer; 46.156 +}
48.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 48.2 +++ b/doc/apache.sample.conf Sun Oct 25 12:00:00 2009 +0100 48.3 @@ -0,0 +1,37 @@ 48.4 +# Apache modules cgi_module, env, rewrite and alias must be loaded before 48.5 +# Take a look in your main apache configuration! 48.6 + 48.7 +RewriteEngine on 48.8 +# do not rewrite static URLs 48.9 +RewriteRule ^/webmcp-demo/static/(.*)$ /webmcp-demo/static/$1 48.10 +# base URL 48.11 +RewriteRule ^/webmcp-demo/(\?(.*))?$ /webmcp-demo/webmcp-wrapper.lua?_webmcp_urldepth=0&_webmcp_module=index&_webmcp_view=index&$2 48.12 +# module base URLs 48.13 +RewriteRule ^/webmcp-demo/([^/\?]+)/(\?(.*))?$ /webmcp-demo/webmcp-wrapper.lua?_webmcp_urldepth=1&_webmcp_module=$1&_webmcp_view=index&$3 48.14 +# actions 48.15 +RewriteRule ^/webmcp-demo/([^/\?]+)/([^/\.\?]+)(\?(.*))?$ /webmcp-demo/webmcp-wrapper.lua?_webmcp_urldepth=1&_webmcp_module=$1&_webmcp_action=$2&$4 48.16 +# views without numeric id or string ident 48.17 +RewriteRule ^/webmcp-demo/([^/\?]+)/([^/\.\?]+)\.([^/\?]+)(\?(.*))?$ "/webmcp-demo/webmcp-wrapper.lua?_webmcp_urldepth=1&_webmcp_module=$1&_webmcp_view=$2&_webmcp_suffix=$3&$5 48.18 +# views with numeric id or string ident 48.19 +RewriteRule ^/webmcp-demo/([^/\?]+)/([^/\?]+)/([^/\.\?]+)\.([^/\?]+)(\?(.*))?$ /webmcp-demo/webmcp-wrapper.lua?_webmcp_urldepth=2&_webmcp_module=$1&_webmcp_view=$2&_webmcp_id=$3&_webmcp_suffix=$4&$6 48.20 + 48.21 +# Directly serve static files 48.22 +Alias /webmcp-demo/static /__INSERT_LOCAL_FILE_PATH_TO_DEMO_APPLICATION_HERE__/static 48.23 + 48.24 +# Connect extarnal path to the webmcp cgi interface 48.25 +ScriptAlias /webmcp-demo/ /__INSERT_LOCAL_FILE_PATH_TO_WEBMCP_FRAMEWORK_HERE__/cgi-bin/ 48.26 + 48.27 +# Allow CGI execution for the webmcp CGI interface 48.28 +<Directory "/__INSERT_LOCAL_FILE_PATH_TO_WEBMCP_FRAMEWORK_HERE__/cgi-bin"> 48.29 + AllowOverride None 48.30 + Options ExecCGI -MultiViews +SymLinksIfOwnerMatch 48.31 + Order allow,deny 48.32 + Allow from all 48.33 +</Directory> 48.34 + 48.35 +# Configure environment for demo application 48.36 +<Location /webmcp-demo> 48.37 + SetEnv WEBMCP_APP_BASEPATH '/__INSERT_LOCAL_FILE_PATH_TO_DEMO_APPLICATION_HERE__' 48.38 + SetEnv WEBMCP_CONFIG_NAME 'demo' 48.39 +</Location> 48.40 +
49.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 49.2 +++ b/doc/autodoc-footer.htmlpart Sun Oct 25 12:00:00 2009 +0100 49.3 @@ -0,0 +1,7 @@ 49.4 + </ul> 49.5 + <hr style="margin-top: 3em;"/> 49.6 + <p style="text-align: right; font-style: italic;"> 49.7 + Copyright (c) 2009 Public Software Group e. V., Berlin 49.8 + </p> 49.9 + </body> 49.10 +</html>
50.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 50.2 +++ b/doc/autodoc-header.htmlpart Sun Oct 25 12:00:00 2009 +0100 50.3 @@ -0,0 +1,306 @@ 50.4 +<html> 50.5 + <head> 50.6 + <style> 50.7 + body { 50.8 + font-family: "Liberation Sans", sans-serif; 50.9 + font-size: 11pt; 50.10 + padding-bottom: 5ex; 50.11 + } 50.12 + .warning { 50.13 + color: #ff0000; 50.14 + } 50.15 + h1, h2 { 50.16 + font-family: "Liberation Serif", Georgia, serif; 50.17 + } 50.18 + h2 { 50.19 + margin-bottom: 0.3ex; 50.20 + } 50.21 + p { 50.22 + margin: 0px; 50.23 + line-height: 130%; 50.24 + } 50.25 + tt, pre { 50.26 + font-size: 10pt; 50.27 + } 50.28 + tt { 50.29 + font-weight: bold; 50.30 + white-space: nowrap; 50.31 + } 50.32 + .autodoc_entry { 50.33 + margin-top: 1ex; 50.34 + margin-bottom: 1ex; 50.35 + } 50.36 + .autodoc_comment_tail { 50.37 + font-style: italic; 50.38 + } 50.39 + .autodoc_entry .short_synopsis { 50.40 + cursor: pointer; 50.41 + } 50.42 + .autodoc_details { 50.43 + padding-left: 1em; 50.44 + padding-right: 1em; 50.45 + border: 1px solid #777; 50.46 + } 50.47 + .autodoc_synopsis { 50.48 + font-weight: bold; 50.49 + } 50.50 + .autodoc_synopsis .autodoc_comment_tail { 50.51 + font-weight: normal; 50.52 + color: #008000; 50.53 + } 50.54 + .autodoc_entry .autodoc_comment { 50.55 + color: #400080; 50.56 + } 50.57 + .autodoc_source { 50.58 + color: #505050; 50.59 + } 50.60 + </style> 50.61 + <title>WebMCP 1.0.0 Documentation</title> 50.62 + </head> 50.63 + <body> 50.64 + <h1>WebMCP 1.0.0 Documentation</h1> 50.65 + <p> 50.66 + WebMCP is a completely new web development framework, and has not been extensively tested yet. The API might change at any time, but in future releases there will be a list of all changes, which break downward compatibility. 50.67 + </p> 50.68 + <h2>Requirements</h2> 50.69 + <p> 50.70 + WebMCP has been developed on Linux and FreeBSD. Using it with Mac OS X is completely untested; Microsoft Windows is not supported. Beside the operating system, the only mandatory dependencies for WebMCP are the programming language <a href="http://www.lua.org/">Lua</a> version 5.1, <a href="http://www.postgresql.org/">PostgreSQL</a> version 8.2 or higher, a C compiler, and a Webserver like Lighttpd or Apache. 50.71 + </p> 50.72 + <h2>Installation</h2> 50.73 + <p> 50.74 + After downloading the tar.gz package, unpack it, enter the unpacked directory and type <tt>make</tt>. If you use Mac OS X or if you experience problems during compilation, you need to edit the <tt>Makefile.options</tt> file prior to compilation. The framework itself will be available in the <tt>framework/</tt> directory, while a demo application is available in the <tt>demo-app/</tt> directory. The <tt>framework.precompiled/</tt> and <tt>demo-app.precompiled/</tt> directories will contain a version with all Lua files being byte-code pre-compiled, which can be used instead. You may copy these directories (with <tt>cp -L</tt> to follow links) to any other place you like. Use the files <tt>doc/lighttpd.sample.conf</tt> or <tt>doc/apache.sample.conf</tt> to setup your webserver appropriatly. Don't forget to setup a database, and make the <tt>tmp/</tt> directory of the application writable for the web server process. Good luck and have fun! 50.75 + </p> 50.76 + <h2>Using the atom library</h2> 50.77 + <p> 50.78 + Lua itself has only very few built-in data types. The atom library gives support for extra data types. Currently the following extra data types are provided: 50.79 + </p> 50.80 + <ul> 50.81 + <li>atom.fraction</li> 50.82 + <li>atom.date</li> 50.83 + <li>atom.time</li> 50.84 + <li>atom.timestamp (date and time combined in one data type)</li> 50.85 + </ul> 50.86 + <p> 50.87 + In addition the following pseudo-types are existent, corresponding to Lua's base types: 50.88 + </p> 50.89 + <ul> 50.90 + <li>atom.boolean</li> 50.91 + <li>atom.string</li> 50.92 + <li>atom.integer</li> 50.93 + <li>atom.number</li> 50.94 + </ul> 50.95 + <p> 50.96 + Both atom.integer and atom.number refer to Lua's base type “number”. 50.97 + </p> 50.98 + <p> 50.99 + New values of atom data types are created by either calling <tt>atom.<i>type</i>:load(string_representation)</tt> or by calling <tt>atom.<i>type</i>{...}</tt>, e.g. <tt>atom.date{year=1970, month=1, day=1}</tt>. You can dump any atom value as a string by calling <tt>atom.dump(value)</tt> and later reload it with <tt>atom.<i>type</i>:load(string)</tt>. 50.100 + </p> 50.101 + <h2>Using the Object-Relational Mapper “mondelefant”</h2> 50.102 + <p> 50.103 + The library “mondelefant” shipping with WebMCP can be used to access PostgreSQL databases. It also serves as an Object-Relational Mapper (ORM). Opening a connection to a database is usually done in a config file in the following way: 50.104 + </p> 50.105 + <pre> 50.106 +db = assert( mondelefant.connect{ engine='postgresql', dbname='webmcp_demo' } ) 50.107 +at_exit(function() 50.108 + db:close() 50.109 +end) 50.110 +function mondelefant.class_prototype:get_db_conn() return db end 50.111 + 50.112 +function db:sql_tracer(command) 50.113 + return function(error_info) 50.114 + local error_info = error_info or {} 50.115 + trace.sql{ command = command, error_position = error_info.position } 50.116 + end 50.117 +end</pre> 50.118 + <p> 50.119 + Overwriting the <tt>sql_tracer</tt> method of the database handle is optional, but helpful for debugging. The parameters for <tt>mondelefant.connect</tt> are directly passed to PostgreSQL's client library libpq. See <a href="http://www.postgresql.org/docs/8.4/interactive/libpq-connect.html">PostgreSQL's documentation on PQconnect</a> for information about supported parameters. 50.120 + </p> 50.121 + <p> 50.122 + To define a model to be used within a WebMCP application, create a file named with the name of the model and <tt>.lua</tt> as extension in the <tt>model/</tt> directory of your application. The most basic definition of a model (named “movie” in this example) is: 50.123 + </p> 50.124 + <pre> 50.125 +Movie = mondelefant.new_class() 50.126 +Movie.table = 'movie'</pre> 50.127 + <p> 50.128 + Note: Model classes are always written CamelCase, while the name of the file in <tt>model/</tt> is written lower_case. 50.129 + </p> 50.130 + <p> 50.131 + To select objects from the database, the mondelefant library provides a selector framework: 50.132 + </p> 50.133 + <pre> 50.134 +local s = Movie:new_selector() 50.135 +s:add_where{ 'id = ?', param.get_id() } 50.136 +s:single_object_mode() -- return single object instead of list 50.137 +local movie = s:exec()</pre> 50.138 + <p> 50.139 + A short form of the above query would be: 50.140 + </p> 50.141 + <pre> 50.142 +local movie = Movie:new_selector():add_where{ 'id = ?', param.get_id() }:single_object_mode():exec()</pre> 50.143 + <p> 50.144 + For more examples about how to use the model system, please take a look at the demo application. 50.145 + </p> 50.146 + <h2>The Model-View-Action (MVA) concept</h2> 50.147 + <p> 50.148 + As opposed to other web application frameworks, WebMCP does not use a Model-View-Controller (MVC) concept, but a Model-View-Action (MVA) concept. 50.149 + </p> 50.150 + <h3>Models</h3> 50.151 + <p> 50.152 + The models in MVA are like the models in MVC; they are used to access data stored in a relational database (PostgreSQL) in an object oriented way. They can also be used to provide methods for working with objects representing the database entries. 50.153 + </p> 50.154 + <h3>Views</h3> 50.155 + <p> 50.156 + The views in the MVA concept are different from the views in the MVC concept. As WebMCP has no controllers, the views are responsible for processing the GET/POST parameters from the webbrowser, fetching the data to be displayed, and creating the output by directly writing HTML to slots in a layout or by calling helper functions for the user interface. 50.157 + </p> 50.158 + <h3>Actions</h3> 50.159 + <p> 50.160 + Actions are similar to views, but supposed to change data in the database, hence only callable by HTTP POST requests. They are also responsible for processing the POST parameters from the webbrowser. They can modify the database, but instead of rendering a page to be displayed, they just return a status code. Depending on the status code there will be an internal forward or an HTTP 303 redirect to a view. When calling an action via a POST request, additional POST parameters, which are usually added by hidden form fields, determine the view to be displayed for each status code returned by the action. 50.161 + </p> 50.162 + <h2>Directory structure of a WebMCP application</h2> 50.163 + <ul> 50.164 + <li> 50.165 + Base Directory 50.166 + <ul> 50.167 + <li> 50.168 + <tt>app/</tt> 50.169 + <ul> 50.170 + <li> 50.171 + <tt>main/</tt> 50.172 + <ul> 50.173 + <li> 50.174 + <tt>_filter/</tt> 50.175 + <ul> 50.176 + <li> 50.177 + <tt>10_first_filter.lua</tt> 50.178 + <li> 50.179 + </li> 50.180 + <tt>30_third_filter.lua</tt> 50.181 + </li> 50.182 + <li>…</li> 50.183 + </ul> 50.184 + </li> 50.185 + <li> 50.186 + <tt>_filter_action/</tt> 50.187 + <ul> 50.188 + <li> 50.189 + <tt>20_second_filter.lua</tt> 50.190 + </li> 50.191 + <li>…</li> 50.192 + </ul> 50.193 + </li> 50.194 + <li> 50.195 + <tt>_filter_view/</tt> 50.196 + <ul> 50.197 + <li>…</li> 50.198 + </ul> 50.199 + </li> 50.200 + <li> 50.201 + <tt>_layout/</tt> 50.202 + <ul> 50.203 + <li>…</li> 50.204 + </ul> 50.205 + </li> 50.206 + <li> 50.207 + <tt>index/</tt> 50.208 + <ul> 50.209 + <li> 50.210 + <tt>_action/</tt> 50.211 + <ul> 50.212 + <li> 50.213 + <i>action_name</i><tt>.lua</tt> 50.214 + </li> 50.215 + <li> 50.216 + <i>another_action_name</i><tt>.lua</tt> 50.217 + </li> 50.218 + <li>…</li> 50.219 + </ul> 50.220 + </li> 50.221 + <li> 50.222 + <tt>index.lua</tt> 50.223 + </li> 50.224 + <li> 50.225 + <i>other_view_name</i><tt>.lua</tt> 50.226 + </li> 50.227 + <li>…</li> 50.228 + </ul> 50.229 + </li> 50.230 + <li> 50.231 + <i>other_module_name</i><tt>/</tt> 50.232 + <ul> 50.233 + <li>…</li> 50.234 + </ul> 50.235 + </li> 50.236 + </ul> 50.237 + </li> 50.238 + <li> 50.239 + <i>other_application_name</i><tt>/</tt> 50.240 + <ul> 50.241 + <li>…</li> 50.242 + </ul> 50.243 + </li> 50.244 + </ul> 50.245 + </li> 50.246 + <li> 50.247 + <tt>config/</tt> 50.248 + <ul> 50.249 + <li> 50.250 + <tt>development.lua</tt> 50.251 + </li> 50.252 + <li> 50.253 + <tt>production.lua</tt> 50.254 + <li> 50.255 + <li> 50.256 + <i>other_config_name</i><tt>.lua</tt> 50.257 + </li> 50.258 + <li>…</li> 50.259 + </ul> 50.260 + </li> 50.261 + <li> 50.262 + <tt>db/</tt> 50.263 + <ul> 50.264 + <li> 50.265 + <tt>schema.sql</tt> 50.266 + </li> 50.267 + </ul> 50.268 + </li> 50.269 + <li> 50.270 + <tt>locale/</tt> 50.271 + <ul> 50.272 + <li> 50.273 + <tt>translations.de.lua</tt> 50.274 + </li> 50.275 + <li> 50.276 + <tt>translations.en.lua</tt> 50.277 + </li> 50.278 + <li> 50.279 + <tt>translations.</tt><i>languagecode</i><tt>.lua</tt> 50.280 + </li> 50.281 + <li>…</li> 50.282 + </ul> 50.283 + </li> 50.284 + <li> 50.285 + <tt>model/</tt> 50.286 + <ul> 50.287 + <li> 50.288 + <i>model_name</i><tt>.lua</tt> 50.289 + </li> 50.290 + <li> 50.291 + <i>another_model_name</i><tt>.lua</tt> 50.292 + </li> 50.293 + <li>…</li> 50.294 + </ul> 50.295 + </li> 50.296 + <li> 50.297 + <tt>static/</tt> 50.298 + <ul> 50.299 + <li>… (images, javascript, ...)</li> 50.300 + </ul> 50.301 + </li> 50.302 + <li> 50.303 + <tt>tmp/</tt> (writable by the web process) 50.304 + </li> 50.305 + </ul> 50.306 + </li> 50.307 + </ul> 50.308 + <h2>Automatically generated reference for the WebMCP environment</h2> 50.309 + <ul>
51.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 51.2 +++ b/doc/lighttpd.sample.conf Sun Oct 25 12:00:00 2009 +0100 51.3 @@ -0,0 +1,60 @@ 51.4 +# Lighttpd modules needed by WebMCP 51.5 +server.modules += ( 51.6 + "mod_cgi", 51.7 + "mod_alias", 51.8 + "mod_setenv", 51.9 + "mod_rewrite", 51.10 + "mod_redirect", 51.11 + ) 51.12 + 51.13 +# Enable CGI-Execution of *.lua files through lua binary 51.14 +cgi.assign += ( ".lua" => "/__INSERT_LOCAL_FILE_PATH_TO_LUA_BINARY_HERE__/lua" ) 51.15 + 51.16 +# Connect external URLs to server static files and the webmcp cgi interface 51.17 +alias.url += ( 51.18 + "/webmcp-demo/static/" => "/__INSERT_LOCAL_FILE_PATH_TO_DEMO_APPLICATION_HERE__/static/", 51.19 + "/webmcp-demo/" => "/__INSERT_LOCAL_FILE_PATH_TO_WEBMCP_FRAMEWORK_HERE__/cgi-bin/" ) 51.20 + 51.21 +# Configure environment for demo application 51.22 +$HTTP["url"] =~ "^/webmcp-demo/" { 51.23 + setenv.add-environment += ( 51.24 + "WEBMCP_APP_BASEPATH" => "/__INSERT_LOCAL_FILE_PATH_TO_DEMO_APPLICATION_HERE__", 51.25 + "WEBMCP_CONFIG_NAME" => "demo") 51.26 +} 51.27 + 51.28 +# URL beautification 51.29 +url.rewrite-once += ( 51.30 + 51.31 + # do not rewrite static URLs 51.32 + "^/webmcp-demo/static/(.*)$" => 51.33 + "/webmcp-demo/static/$1", 51.34 + 51.35 + # base URL 51.36 + "^/webmcp-demo/(\?(.*))?$" => 51.37 + "/webmcp-demo/webmcp-wrapper.lua?_webmcp_urldepth=0&_webmcp_module=index&_webmcp_view=index&$2", 51.38 + 51.39 + # module base URLs 51.40 + "^/webmcp-demo/([^/\?]+)/(\?(.*))?$" => 51.41 + "/webmcp-demo/webmcp-wrapper.lua?_webmcp_urldepth=1&_webmcp_module=$1&_webmcp_view=index&$3", 51.42 + 51.43 + # actions 51.44 + "^/webmcp-demo/([^/\?]+)/([^/\.\?]+)(\?(.*))?$" => 51.45 + "/webmcp-demo/webmcp-wrapper.lua?_webmcp_urldepth=1&_webmcp_module=$1&_webmcp_action=$2&$4", 51.46 + 51.47 + # views without numeric id or string ident 51.48 + "^/webmcp-demo/([^/\?]+)/([^/\.\?]+)\.([^/\?]+)(\?(.*))?$" => 51.49 + "/webmcp-demo/webmcp-wrapper.lua?_webmcp_urldepth=1&_webmcp_module=$1&_webmcp_view=$2&_webmcp_suffix=$3&$5", 51.50 + 51.51 + # views with numeric id or string ident 51.52 + "^/webmcp-demo/([^/\?]+)/([^/\?]+)/([^/\.\?]+)\.([^/\?]+)(\?(.*))?$" => 51.53 + "/webmcp-demo/webmcp-wrapper.lua?_webmcp_urldepth=2&_webmcp_module=$1&_webmcp_view=$2&_webmcp_id=$3&_webmcp_suffix=$4&$6", 51.54 + 51.55 +) 51.56 + 51.57 +# Redirects for URLs without trailing slashes 51.58 +url.redirect += ( 51.59 + # base URL without trailing slash 51.60 + "^/webmcp-demo$" => "/webmcp-demo/", 51.61 + # module base URL without trailing slash 51.62 + "^/webmcp-demo/([^/\?]+)$" => "/webmcp-demo/$1/", 51.63 +)
52.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 52.2 +++ b/framework/accelerator/Makefile Sun Oct 25 12:00:00 2009 +0100 52.3 @@ -0,0 +1,10 @@ 52.4 +include ../../Makefile.options 52.5 + 52.6 +webmcp_accelerator.so: webmcp_accelerator.o 52.7 + $(LD) $(LDFLAGS) -o webmcp_accelerator.$(SLIB_EXT) webmcp_accelerator.o 52.8 + 52.9 +webmcp_accelerator.o: webmcp_accelerator.c 52.10 + $(CC) -c $(CFLAGS) -o webmcp_accelerator.o webmcp_accelerator.c 52.11 + 52.12 +clean:: 52.13 + rm -f webmcp_accelerator.so webmcp_accelerator.o
53.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 53.2 +++ b/framework/accelerator/webmcp_accelerator.c Sun Oct 25 12:00:00 2009 +0100 53.3 @@ -0,0 +1,196 @@ 53.4 +#include <lua.h> 53.5 +#include <lauxlib.h> 53.6 +#include <stdlib.h> 53.7 +#include <string.h> 53.8 + 53.9 +static int webmcp_encode_html(lua_State *L) { 53.10 + const char *input; 53.11 + size_t input_len; 53.12 + size_t i; 53.13 + luaL_Buffer buf; 53.14 + input = luaL_checklstring(L, 1, &input_len); 53.15 + for (i=0; i<input_len; i++) { 53.16 + char c = input[i]; 53.17 + if ((c == '<') || (c == '>') || (c == '&') || (c == '"')) break; 53.18 + } 53.19 + if (i == input_len) { 53.20 + lua_settop(L, 1); 53.21 + return 1; 53.22 + } 53.23 + luaL_buffinit(L, &buf); 53.24 + for (i=0; i<input_len; i++) { 53.25 + char c; 53.26 + size_t j = i; 53.27 + do { 53.28 + c = input[j]; 53.29 + if ((c == '<') || (c == '>') || (c == '&') || (c == '"')) break; 53.30 + else j++; 53.31 + } while (j<input_len); 53.32 + if (j != i) { 53.33 + luaL_addlstring(&buf, input+i, j-i); 53.34 + i = j; 53.35 + } 53.36 + if (i<input_len) { 53.37 + if (c == '<') luaL_addstring(&buf, "<"); 53.38 + else if (c == '>') luaL_addstring(&buf, ">"); 53.39 + else if (c == '&') luaL_addstring(&buf, "&"); 53.40 + else if (c == '"') luaL_addstring(&buf, """); 53.41 + else abort(); // should not happen 53.42 + } 53.43 + } 53.44 + luaL_pushresult(&buf); 53.45 + return 1; 53.46 +} 53.47 + 53.48 +static int webmcp_slot_put_into(lua_State *L) { 53.49 + int argc; 53.50 + int i; 53.51 + int j; 53.52 + luaL_checkany(L, 1); 53.53 + argc = lua_gettop(L); 53.54 + lua_getglobal(L, "slot"); 53.55 + lua_getfield(L, -1, "_data"); 53.56 + lua_pushvalue(L, 1); 53.57 + lua_gettable(L, -2); // get table by reference passed as 1st argument 53.58 + lua_getfield(L, -1, "string_fragments"); 53.59 + j = lua_objlen(L, -1); 53.60 + for (i=2; i<=argc; i++) { 53.61 + lua_pushvalue(L, i); 53.62 + lua_rawseti(L, -2, ++j); 53.63 + } 53.64 + return 0; 53.65 +} 53.66 + 53.67 +static int webmcp_slot_put(lua_State *L) { 53.68 + int argc; 53.69 + int i; 53.70 + int j; 53.71 + argc = lua_gettop(L); 53.72 + lua_getglobal(L, "slot"); 53.73 + lua_getfield(L, -1, "_data"); 53.74 + lua_getfield(L, -2, "_active_slot"); 53.75 + lua_gettable(L, -2); 53.76 + lua_getfield(L, -1, "string_fragments"); 53.77 + j = lua_objlen(L, -1); 53.78 + for (i=1; i<=argc; i++) { 53.79 + lua_pushvalue(L, i); 53.80 + lua_rawseti(L, -2, ++j); 53.81 + } 53.82 + return 0; 53.83 +} 53.84 + 53.85 +static int webmcp_ui_tag(lua_State *L) { 53.86 + int tag_given = 0; 53.87 + int j; 53.88 + lua_settop(L, 1); 53.89 + luaL_checktype(L, 1, LUA_TTABLE); 53.90 + lua_getglobal(L, "slot"); // 2 53.91 + lua_getfield(L, 2, "_data"); // 3 53.92 + lua_getfield(L, 2, "_active_slot"); 53.93 + lua_gettable(L, 3); // 4 53.94 + lua_getfield(L, 4, "string_fragments"); // 5 53.95 + lua_getfield(L, 1, "tag"); // 6 53.96 + lua_getfield(L, 1, "attr"); // 7 53.97 + lua_getfield(L, 1, "content"); // 8 53.98 + if (lua_toboolean(L, 7) && !lua_istable(L, 7)) { 53.99 + return luaL_error(L, 53.100 + "\"attr\" argument for ui.tag{...} must be nil or a table." 53.101 + ); 53.102 + } 53.103 + if (lua_toboolean(L, 6)) { 53.104 + tag_given = 1; 53.105 + } else if (lua_toboolean(L, 7)) { 53.106 + lua_pushnil(L); 53.107 + if (lua_next(L, 7)) { 53.108 + lua_pop(L, 2); 53.109 + lua_pushliteral(L, "span"); 53.110 + lua_replace(L, 6); 53.111 + tag_given = 1; 53.112 + } 53.113 + } 53.114 + j = lua_objlen(L, 5); 53.115 + if (tag_given) { 53.116 + lua_pushliteral(L, "<"); 53.117 + lua_rawseti(L, 5, ++j); 53.118 + lua_pushvalue(L, 6); 53.119 + lua_rawseti(L, 5, ++j); 53.120 + if (lua_toboolean(L, 7)) { 53.121 + for (lua_pushnil(L); lua_next(L, 7); lua_pop(L, 1)) { 53.122 + // key at position 9 53.123 + // value at position 10 53.124 + lua_pushliteral(L, " "); 53.125 + lua_rawseti(L, 5, ++j); 53.126 + lua_pushvalue(L, 9); 53.127 + lua_rawseti(L, 5, ++j); 53.128 + lua_pushliteral(L, "=\""); 53.129 + lua_rawseti(L, 5, ++j); 53.130 + if (!strcmp(lua_tostring(L, 9), "class") && lua_istable(L, 10)) { // NOTE: lua_tostring(...) is destructive, will cause errors for numeric keys 53.131 + lua_getglobal(L, "table"); // 11 53.132 + lua_getfield(L, 11, "concat"); // 12 53.133 + lua_replace(L, 11); // 11 53.134 + lua_pushvalue(L, 10); // 12 53.135 + lua_pushliteral(L, " "); // 13 53.136 + lua_call(L, 2, 1); // 11 53.137 + lua_rawseti(L, 5, ++j); 53.138 + } else { 53.139 + lua_pushcfunction(L, webmcp_encode_html); 53.140 + lua_pushvalue(L, 10); 53.141 + lua_call(L, 1, 1); 53.142 + lua_rawseti(L, 5, ++j); 53.143 + } 53.144 + lua_pushliteral(L, "\""); 53.145 + lua_rawseti(L, 5, ++j); 53.146 + } 53.147 + } 53.148 + } 53.149 + if (lua_toboolean(L, 8)) { 53.150 + if (tag_given) { 53.151 + lua_pushliteral(L, ">"); 53.152 + lua_rawseti(L, 5, ++j); 53.153 + } 53.154 + if (lua_isfunction(L, 8)) { 53.155 + // content function should be on last stack position 8 53.156 + lua_call(L, 0, 0); 53.157 + // stack is now at position 7, but we don't care 53.158 + // we assume that the active slot hasn't been exchanged or resetted 53.159 + j = lua_objlen(L, 5); // but it may include more elements now 53.160 + } else { 53.161 + lua_pushcfunction(L, webmcp_encode_html); // 9 53.162 + lua_pushvalue(L, 8); // 10 53.163 + lua_call(L, 1, 1); // 9 53.164 + lua_rawseti(L, 5, ++j); 53.165 + } 53.166 + if (tag_given) { 53.167 + lua_pushliteral(L, "</"); 53.168 + lua_rawseti(L, 5, ++j); 53.169 + lua_pushvalue(L, 6); 53.170 + lua_rawseti(L, 5, ++j); 53.171 + lua_pushliteral(L, ">"); 53.172 + lua_rawseti(L, 5, ++j); 53.173 + } 53.174 + } else { 53.175 + if (tag_given) { 53.176 + lua_pushliteral(L, " />"); 53.177 + lua_rawseti(L, 5, ++j); 53.178 + } 53.179 + } 53.180 + return 0; 53.181 +} 53.182 + 53.183 +int luaopen_webmcp_accelerator(lua_State *L) { 53.184 + lua_settop(L, 0); 53.185 + lua_getglobal(L, "encode"); // 1 53.186 + lua_pushcfunction(L, webmcp_encode_html); 53.187 + lua_setfield(L, 1, "html"); 53.188 + lua_settop(L, 0); 53.189 + lua_getglobal(L, "slot"); // 1 53.190 + lua_pushcfunction(L, webmcp_slot_put_into); 53.191 + lua_setfield(L, 1, "put_into"); 53.192 + lua_pushcfunction(L, webmcp_slot_put); 53.193 + lua_setfield(L, 1, "put"); 53.194 + lua_settop(L, 0); 53.195 + lua_getglobal(L, "ui"); // 1 53.196 + lua_pushcfunction(L, webmcp_ui_tag); 53.197 + lua_setfield(L, 1, "tag"); 53.198 + return 0; 53.199 +}
54.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 54.2 +++ b/framework/bin/autodoc.lua Sun Oct 25 12:00:00 2009 +0100 54.3 @@ -0,0 +1,216 @@ 54.4 +#!/usr/bin/env lua 54.5 + 54.6 +local args = {...} 54.7 + 54.8 +if not args[1] or string.match(args[1], "^%-") then 54.9 + print("Usage: autodoc.lua srcdir/ > documentation.html") 54.10 + os.exit(1) 54.11 +end 54.12 + 54.13 +local entries = {} 54.14 + 54.15 +for idx, srcdir in ipairs(args) do 54.16 + local find_proc = io.popen('find "' .. srcdir .. '" -name \\*.lua', "r") 54.17 + for filename in find_proc:lines() do 54.18 + local synopsis, comment, source 54.19 + local mode 54.20 + local function reset() 54.21 + synopsis, comment, source = {}, {}, {} 54.22 + mode = "idle" 54.23 + end 54.24 + reset() 54.25 + local function strip(tbl) 54.26 + while true do 54.27 + local line = tbl[#tbl] 54.28 + if line and string.find(line, "^%s*$") then 54.29 + tbl[#tbl] = nil 54.30 + else 54.31 + break 54.32 + end 54.33 + end 54.34 + if #tbl > 0 then 54.35 + local min_indent = math.huge 54.36 + for idx, line in ipairs(tbl) do 54.37 + local spaces = string.match(line, "^(%s*)") 54.38 + if min_indent > #spaces then 54.39 + min_indent = #spaces 54.40 + end 54.41 + end 54.42 + local pattern_parts = { "^" } 54.43 + for i = 1, min_indent do 54.44 + pattern_parts[#pattern_parts+1] = "%s" 54.45 + end 54.46 + pattern_parts[#pattern_parts+1] = "(.-)%s*$" 54.47 + local pattern = table.concat(pattern_parts) 54.48 + for idx, line in ipairs(tbl) do 54.49 + tbl[idx] = string.match(line, pattern) 54.50 + end 54.51 + end 54.52 + end 54.53 + local function entry_done() 54.54 + if #synopsis > 0 then 54.55 + strip(synopsis) 54.56 + strip(comment) 54.57 + strip(source) 54.58 + local stripped_synopsis = {} 54.59 + for idx, line in ipairs(synopsis) do 54.60 + local stripped_line = string.match(line, "^(.-)%-%-") or line 54.61 + stripped_line = string.match(stripped_line, "^(.-)%s*$") 54.62 + stripped_synopsis[#stripped_synopsis+1] = stripped_line 54.63 + end 54.64 + local concatted_synopsis = string.gsub( 54.65 + table.concat(stripped_synopsis, " "), "[%s]+", " " 54.66 + ) 54.67 + local func_call = string.match( 54.68 + concatted_synopsis, "^[A-Za-z0-9_, ]+= ?(.-) ?$" 54.69 + ) 54.70 + if not func_call then 54.71 + func_call = string.match( 54.72 + concatted_synopsis, 54.73 + "^ ?for[A-Za-z0-9_, ]+in (.-) ? do[ %.]+end ?$" 54.74 + ) 54.75 + end 54.76 + if not func_call then 54.77 + func_call = string.match(concatted_synopsis, "^ ?(.-) ?$") 54.78 + end 54.79 + func_call = string.gsub( 54.80 + func_call, 54.81 + "^([^({]*)[({].*[,;].*[,;].*[,;].*[)}]$", 54.82 + function(base) 54.83 + return base .. "{ ... }" 54.84 + end 54.85 + ) 54.86 + if entries[func_call] then 54.87 + error("Multiple occurrences of: " .. func_call) 54.88 + end 54.89 + entries[func_call] = { 54.90 + func_call = func_call, 54.91 + synopsis = synopsis, 54.92 + comment = comment, 54.93 + source = source 54.94 + } 54.95 + end 54.96 + reset() 54.97 + end 54.98 + for line in io.lines(filename, "r") do 54.99 + local function add_to(tbl) 54.100 + if #tbl > 0 or not string.match(line, "^%s*$") then 54.101 + tbl[#tbl+1] = line 54.102 + end 54.103 + end 54.104 + if mode == "idle" then 54.105 + if string.find(line, "^%s*%-%-%[%[%-%-%s*$") then 54.106 + mode = "synopsis" 54.107 + end 54.108 + elseif mode == "synopsis" then 54.109 + if string.find(line, "^%s*$") and #synopsis > 0 then 54.110 + mode = "comment" 54.111 + elseif string.find(line, "^%s*%-%-]]%-%-%s*$") then 54.112 + mode = "source" 54.113 + else 54.114 + add_to(synopsis) 54.115 + end 54.116 + elseif mode == "comment" then 54.117 + if string.find(line, "^%s*%-%-]]%-%-%s*$") then 54.118 + mode = "source" 54.119 + else 54.120 + add_to(comment) 54.121 + end 54.122 + elseif mode == "source" then 54.123 + if string.find(line, "^%s*%-%-//%-%-%s*$") then 54.124 + entry_done() 54.125 + else 54.126 + add_to(source) 54.127 + end 54.128 + end 54.129 + end 54.130 + entry_done() 54.131 + end 54.132 + find_proc:close() 54.133 +end 54.134 + 54.135 + 54.136 +function output(...) 54.137 + return io.stdout:write(...) 54.138 +end 54.139 + 54.140 +function encode(text) 54.141 + return ( 54.142 + string.gsub( 54.143 + text, '[<>&"]', 54.144 + function(char) 54.145 + if char == '<' then 54.146 + return "<" 54.147 + elseif char == '>' then 54.148 + return ">" 54.149 + elseif char == '&' then 54.150 + return "&" 54.151 + elseif char == '"' then 54.152 + return """ 54.153 + end 54.154 + end 54.155 + ) 54.156 + ) 54.157 +end 54.158 + 54.159 +function output_lines(tbl) 54.160 + for idx, line in ipairs(tbl) do 54.161 + if idx == 1 then 54.162 + output('<pre>') 54.163 + end 54.164 + local command, comment = string.match(line, "^(.-)(%-%-.*)$") 54.165 + if command then 54.166 + output( 54.167 + encode(command), 54.168 + '<span class="autodoc_comment_tail">', encode(comment), '</span>' 54.169 + ) 54.170 + else 54.171 + output(encode(line)) 54.172 + end 54.173 + if idx == #tbl then 54.174 + output('</pre>') 54.175 + end 54.176 + output('\n') 54.177 + end 54.178 +end 54.179 + 54.180 +keys = {} 54.181 +for key in pairs(entries) do 54.182 + keys[#keys+1] = key 54.183 +end 54.184 +table.sort(keys) 54.185 +for idx, key in ipairs(keys) do 54.186 + local entry = entries[key] 54.187 + output('<li class="autodoc_entry">\n') 54.188 + output( 54.189 + ' <div class="short_synopsis"', 54.190 + ' onclick="document.getElementById(\'autodoc_details_', 54.191 + idx, 54.192 + '\').style.display = document.getElementById(\'autodoc_details_', 54.193 + idx, 54.194 + '\').style.display ? \'\' : \'none\';">\n' 54.195 + ) 54.196 + output(' ', encode(entry.func_call), '\n') 54.197 + output(' </div>\n') 54.198 + output( 54.199 + ' <div id="autodoc_details_', 54.200 + idx, 54.201 + '" class="autodoc_details" style="display: none;">\n' 54.202 + ) 54.203 + output(' <div class="autodoc_synopsis">\n') 54.204 + output_lines(entry.synopsis) 54.205 + output(' </div>\n') 54.206 + output(' <div class="autodoc_comment">') 54.207 + for idx, line in ipairs(entry.comment) do 54.208 + output(encode(line)) 54.209 + if idx < #entry.comment then 54.210 + output('<br/>') 54.211 + end 54.212 + end 54.213 + output(' </div>\n') 54.214 + output(' <div class="autodoc_source">\n') 54.215 + output_lines(entry.source) 54.216 + output(' </div>\n') 54.217 + output(' </div>\n') 54.218 + output('</li>\n') 54.219 +end 54.220 \ No newline at end of file
55.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 55.2 +++ b/framework/bin/langtool.lua Sun Oct 25 12:00:00 2009 +0100 55.3 @@ -0,0 +1,188 @@ 55.4 +#!/usr/bin/env lua 55.5 + 55.6 +if not pcall( 55.7 + function() 55.8 + require "extos" 55.9 + end 55.10 +) then 55.11 + io.stderr:write('Could not load library "extos".\n') 55.12 + io.stderr:write('Hint: Set LUA_CPATH="/path_to_extos_library/?.so;;"\n') 55.13 +end 55.14 + 55.15 + 55.16 +local args = {...} 55.17 + 55.18 +if #args == 0 then 55.19 + print() 55.20 + print("This program creates translation files by traversing source directories.") 55.21 + print() 55.22 + print("Two formats are supported: lua files and po files.") 55.23 + print("At runtime a lua file is needed.") 55.24 + print("For use with po-file editors you may want to create po files first though.") 55.25 + print() 55.26 + print("Create or update a lua file: langtool.lua dir1/ dir2/ ... <basename>.lua") 55.27 + print("Create or update a po file: langtool.lua dir1/ dir2/ ... <basename>.po") 55.28 + print("Convert po file to lua file: langtool.lua <basename>.po <basename>.lua") 55.29 + print("Convert lua file to po file: langtool.lua <basename>.lua <basename>.po") 55.30 + print() 55.31 +end 55.32 + 55.33 +local in_filename, in_filetype, out_filename, out_filetype 55.34 +local directories = {} 55.35 + 55.36 +for arg_num, arg in ipairs(args) do 55.37 + local function arg_error(msg) 55.38 + error("Illegal command line argument #" .. arg_num .. ": " .. msg) 55.39 + end 55.40 + local po = string.match(arg, "^po:(.*)$") or string.match(arg, "^(.*%.po)$") 55.41 + local lua = string.match(arg, "^lua:(.*)$") or string.match(arg, "^(.*%.lua)$") 55.42 + local filetype 55.43 + if po and not lua then filetype = "po" 55.44 + filetype = "po" 55.45 + elseif lua and not po then filetype = "lua" 55.46 + filetype = "lua" 55.47 + else 55.48 + filetype = "path" 55.49 + end 55.50 + if filetype == "path" then 55.51 + table.insert(directories, arg) 55.52 + elseif filetype == "po" or filetype == "lua" then 55.53 + if not out_filename then 55.54 + out_filename = arg 55.55 + out_filetype = filetype 55.56 + elseif not in_filename then 55.57 + in_filename = out_filename 55.58 + in_filetype = out_filetype 55.59 + out_filename = arg 55.60 + out_filetype = filetype 55.61 + else 55.62 + arg_error("Only two language files (one input and one output file) can be specified.") 55.63 + end 55.64 + else 55.65 + -- should not happen, as default type is "path" 55.66 + arg_error("Type not recognized") 55.67 + end 55.68 +end 55.69 + 55.70 +if #directories > 0 and not os.listdir then 55.71 + io.stderr:write('Fatal: Cannot traverse directories without "extos" library -> Abort\n') 55.72 + os.exit(1) 55.73 +end 55.74 + 55.75 +if out_filename and not in_filename then 55.76 + local file = io.open(out_filename, "r") 55.77 + if file then 55.78 + in_filename = out_filename 55.79 + in_filetype = out_filetype 55.80 + file:close() 55.81 + end 55.82 +end 55.83 + 55.84 +local translations = { } 55.85 + 55.86 +local function traverse(path) 55.87 + local filenames = os.listdir(path) 55.88 + if not filenames then return false end 55.89 + for num, filename in ipairs(filenames) do 55.90 + if not string.find(filename, "^%.") then 55.91 + if string.find(filename, "%.lua$") then 55.92 + for line in io.lines(path .. "/" .. filename) do 55.93 + -- TODO: exact parsing of comments and escape characters 55.94 + for key in string.gmatch(line, "_%(?'([^'\]+)'") do 55.95 + if 55.96 + key ~= "([^" and 55.97 + (not string.find(key, "^%s*%.%.[^%.]")) and 55.98 + (not string.find(key, "^%s*,[^,]")) 55.99 + then 55.100 + translations[key] = false 55.101 + end 55.102 + end 55.103 + for key in string.gmatch(line, '_%(?"([^"\]+)"') do 55.104 + if 55.105 + key ~= "([^" and 55.106 + (not string.find(key, "^%s*%.%.[^%.]")) and 55.107 + (not string.find(key, "^%s*,[^,]")) 55.108 + then 55.109 + translations[key] = false 55.110 + end 55.111 + end 55.112 + end 55.113 + end 55.114 + traverse(path .. "/" .. filename) 55.115 + end 55.116 + end 55.117 + return true 55.118 +end 55.119 +for num, directory in ipairs(directories) do 55.120 + io.stderr:write('Parsing files in directory "', directory, '".\n') 55.121 + if not traverse(directory) then 55.122 + error('Could not read directory "' .. directory .. '".') 55.123 + end 55.124 +end 55.125 + 55.126 +local function update_translation(key, value) 55.127 + if #directories > 0 then 55.128 + if translations[key] ~= nil then translations[key] = value end 55.129 + else 55.130 + translations[key] = value 55.131 + end 55.132 +end 55.133 + 55.134 +if in_filetype == "po" then 55.135 + io.stderr:write('Reading translations from po file "', in_filename, '".\n') 55.136 + local next_line = io.lines(in_filename) 55.137 + for line in next_line do 55.138 + if not line then break end 55.139 + local key = string.match(line, '^msgid%s*"(.*)"%s*$') 55.140 + if key then 55.141 + local line = next_line() 55.142 + local value = string.match(line, '^msgstr%s*"(.*)"%s*$') 55.143 + if not value then 55.144 + error("Expected msgstr line in po file.") 55.145 + end 55.146 + if translations[key] then 55.147 + error("Duplicate key '" .. key .. "' in po file.") 55.148 + end 55.149 + if value == "" then value = false end 55.150 + update_translation(key, value) 55.151 + end 55.152 + end 55.153 +elseif in_filetype == "lua" then 55.154 + io.stderr:write('Reading translations from lua file "', in_filename, '".\n') 55.155 + local func = assert(loadfile(in_filename)) 55.156 + setfenv(func, {}) 55.157 + local updates = func() 55.158 + for key, value in pairs(updates) do 55.159 + update_translation(key, value) 55.160 + end 55.161 +end 55.162 + 55.163 +local translation_keys = {} 55.164 +for key in pairs(translations) do 55.165 + table.insert(translation_keys, key) 55.166 +end 55.167 +table.sort(translation_keys) 55.168 + 55.169 +if out_filetype == "po" then 55.170 + io.stderr:write('Writing translations to po file "', out_filename, '".\n') 55.171 + local file = assert(io.open(out_filename, "w")) 55.172 + for num, key in ipairs(translation_keys) do 55.173 + local value = translations[key] 55.174 + file:write('msgid "', key, '"\nmsgstr "', value or "", '"\n\n') 55.175 + end 55.176 + io.close(file) 55.177 +elseif out_filetype == "lua" then 55.178 + io.stderr:write('Writing translations to lua file "', out_filename, '".\n') 55.179 + local file = assert(io.open(out_filename, "w")) 55.180 + file:write("#!/usr/bin/env lua\n", "return {\n") 55.181 + for num, key in ipairs(translation_keys) do 55.182 + local value = translations[key] 55.183 + if value then 55.184 + file:write(string.format("[%q] = %q;\n", key, value)) 55.185 + else 55.186 + file:write(string.format("[%q] = false;\n", key)) 55.187 + end 55.188 + end 55.189 + file:write("}\n") 55.190 + io.close(file) 55.191 +end
56.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 56.2 +++ b/framework/bin/recursive-luac Sun Oct 25 12:00:00 2009 +0100 56.3 @@ -0,0 +1,14 @@ 56.4 +#!/bin/sh 56.5 +src=$1 56.6 +dst=$2 56.7 +if [ "$src" = "" -o "$dst" = "" ] 56.8 +then 56.9 + echo "Usage: recursive-luac <source-dir> <destination-dir>" 56.10 + exit 1 56.11 +fi 56.12 +cp -RL "$src" "$dst" 56.13 +# TODO: handle whitespace in directory or file names correctly 56.14 +for file in `find "$dst" -name '*.lua'` 56.15 +do 56.16 + luac -s -o "$file" "$file" 56.17 +done
57.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 57.2 +++ b/framework/bin/webmcp_shell Sun Oct 25 12:00:00 2009 +0100 57.3 @@ -0,0 +1,10 @@ 57.4 +#!/bin/sh 57.5 +export WEBMCP_APP_BASEPATH="`pwd`" 57.6 +export WEBMCP_CONFIG_NAME="$1" 57.7 +export WEBMCP_INTERACTIVE="yes" 57.8 +if [ "$WEBMCP_CONFIG_NAME" = "" ]; then 57.9 + echo "Error: No config name given." > /dev/stderr 57.10 + exit 1 57.11 +fi 57.12 +cd "`dirname "$0"`/../cgi-bin" 57.13 +exec lua -i webmcp.lua
58.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 58.2 +++ b/framework/cgi-bin/webmcp-wrapper.lua Sun Oct 25 12:00:00 2009 +0100 58.3 @@ -0,0 +1,23 @@ 58.4 +#!/usr/bin/env lua 58.5 + 58.6 +local func, errmsg = loadfile('webmcp.lua') 58.7 + 58.8 +if func then 58.9 + local result 58.10 + result, errmsg = pcall(func) 58.11 + if result then 58.12 + errmsg = nil 58.13 + end 58.14 +end 58.15 + 58.16 +if errmsg and not (cgi and cgi.data_sent) then 58.17 + print('Status: 500 Internal Server Error') 58.18 + print('Content-type: text/plain') 58.19 + print() 58.20 + print('500 Internal Server Error') 58.21 + print() 58.22 + print(errmsg) 58.23 + print() 58.24 + print('(caught by webmcp-wrapper.lua)') 58.25 + os.exit(1) 58.26 +end
59.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 59.2 +++ b/framework/cgi-bin/webmcp.lua Sun Oct 25 12:00:00 2009 +0100 59.3 @@ -0,0 +1,449 @@ 59.4 +#!/usr/bin/env lua 59.5 + 59.6 +-- include "../lib/" in search path for libraries 59.7 +do 59.8 + package.path = '../lib/?.lua;' .. package.path 59.9 + -- find out which file name extension shared libraries have 59.10 + local slib_exts = {} 59.11 + for ext in string.gmatch(package.cpath, "%?%.([A-Za-z0-9_-]+)") do 59.12 + slib_exts[ext] = true 59.13 + end 59.14 + local paths = {} 59.15 + for ext in pairs(slib_exts) do 59.16 + paths[#paths+1] = "../accelerator/?." .. ext 59.17 + end 59.18 + for ext in pairs(slib_exts) do 59.19 + paths[#paths+1] = "../lib/?." .. ext 59.20 + end 59.21 + paths[#paths+1] = package.cpath 59.22 + package.cpath = table.concat(paths, ";") 59.23 +end 59.24 + 59.25 +-- load os extensions for lua 59.26 +-- (should happen as soon as possible due to run time measurement) 59.27 +require 'extos' 59.28 + 59.29 +-- load nihil library 59.30 +require 'nihil' 59.31 + 59.32 +-- load random generator library 59.33 +require 'multirand' 59.34 + 59.35 +-- load rocketcgi library and map it to cgi 59.36 +do 59.37 + local option = os.getenv("WEBMCP_INTERACTIVE") 59.38 + if option and option ~= "" and option ~= "0" then 59.39 + cgi = nil 59.40 + else 59.41 + require 'rocketcgi' 59.42 + cgi = rocketcgi 59.43 + end 59.44 +end 59.45 + 59.46 +-- load database access library with object relational mapper 59.47 +require 'mondelefant' 59.48 +mondelefant.connection_prototype.error_objects = true 59.49 + 59.50 +-- load type system "atom" 59.51 +require 'atom' 59.52 + 59.53 +-- load mondelefant atom connector 59.54 +require 'mondelefant_atom_connector' 59.55 + 59.56 +--[[-- 59.57 +cloned_table = -- newly generated table 59.58 +table.new( 59.59 + table_or_nil -- keys of a given table will be copied to the new table 59.60 +) 59.61 + 59.62 +If a table is given, then a cloned table is returned. 59.63 +If nil is given, then a new empty table is returned. 59.64 + 59.65 +--]]-- 59.66 +function table.new(tbl) 59.67 + new_tbl = {} 59.68 + if tbl then 59.69 + for key, value in pairs(tbl) do 59.70 + new_tbl[key] = value 59.71 + end 59.72 + end 59.73 + return new_tbl 59.74 +end 59.75 +--//-- 59.76 + 59.77 +--[[-- 59.78 +at_exit( 59.79 + func -- function to be called before the process is ending 59.80 +) 59.81 + 59.82 +Registers a function to be called before the CGI process is exiting. 59.83 +--]]-- 59.84 +do 59.85 + local exit_handlers = {} 59.86 + function at_exit(func) 59.87 + table.insert(exit_handlers, func) 59.88 + end 59.89 + function exit(code) 59.90 + for i = #exit_handlers, 1, -1 do 59.91 + exit_handlers[i]() 59.92 + end 59.93 + os.exit(code) 59.94 + end 59.95 +end 59.96 +--//-- 59.97 + 59.98 +--[[-- 59.99 +app -- table to store an application state 59.100 + 59.101 +'app' is a global table for storing any application state data 59.102 +--]]-- 59.103 +app = {} 59.104 +--//-- 59.105 + 59.106 +--[[-- 59.107 +config -- table to store application configuration 59.108 + 59.109 +'config' is a global table, which can be modified by a config file of an application to modify the behaviour of that application. 59.110 +--]]-- 59.111 +config = {} 59.112 +--//-- 59.113 + 59.114 +-- autoloader system for WebMCP environment "../env/", 59.115 +-- application environment extensions "$WEBMCP_APP_BASE/env/" 59.116 +-- and models "$WEBMCP_APP_BASE/model/" 59.117 +do 59.118 + local app_base = os.getenv("WEBMCP_APP_BASEPATH") 59.119 + if not app_base then 59.120 + error( 59.121 + "Failed to initialize autoloader " .. 59.122 + "due to unset WEBMCP_APP_BASEPATH environment variable." 59.123 + ) 59.124 + end 59.125 + local weakkey_mt = { __mode = "k" } 59.126 + local autoloader_category = setmetatable({}, weakkey_mt) 59.127 + local autoloader_path = setmetatable({}, weakkey_mt) 59.128 + local autoloader_mt = {} 59.129 + local function install_autoloader(self, category, path) 59.130 + autoloader_category[self] = category 59.131 + autoloader_path[self] = path 59.132 + setmetatable(self, autoloader_mt) 59.133 + end 59.134 + local function try_exec(filename) 59.135 + local file = io.open(filename, "r") 59.136 + if file then 59.137 + local filedata = file:read("*a") 59.138 + io.close(file) 59.139 + local func, errmsg = loadstring(filedata, "=" .. filename) 59.140 + if func then 59.141 + func() 59.142 + return true 59.143 + else 59.144 + error(errmsg, 0) 59.145 + end 59.146 + else 59.147 + return false 59.148 + end 59.149 + end 59.150 + local function compose_path_string(base, path, key) 59.151 + return string.gsub( 59.152 + base .. table.concat(path, "/") .. "/" .. key, "/+", "/" 59.153 + ) 59.154 + end 59.155 + function autoloader_mt.__index(self, key) 59.156 + local category, base_path, merge_base_path, file_key 59.157 + local merge = false 59.158 + if 59.159 + string.find(key, "^[a-z_][A-Za-z0-9_]*$") and 59.160 + not string.find(key, "^__") 59.161 + then 59.162 + category = "env" 59.163 + base_path = "../env/" 59.164 + merge = true 59.165 + merge_base_path = app_base .. "/env/" 59.166 + file_key = key 59.167 + elseif string.find(key, "^[A-Z][A-Za-z0-9]*$") then 59.168 + category = "model" 59.169 + base_path = app_base .. "/model/" 59.170 + local first = true 59.171 + file_key = string.gsub(key, "[A-Z]", 59.172 + function(c) 59.173 + if first then 59.174 + first = false 59.175 + return string.lower(c) 59.176 + else 59.177 + return "_" .. string.lower(c) 59.178 + end 59.179 + end 59.180 + ) 59.181 + else 59.182 + return 59.183 + end 59.184 + local required_category = autoloader_category[self] 59.185 + if required_category and required_category ~= category then return end 59.186 + local path = autoloader_path[self] 59.187 + local path_string = compose_path_string(base_path, path, file_key) 59.188 + local merge_path_string 59.189 + if merge then 59.190 + merge_path_string = compose_path_string( 59.191 + merge_base_path, path, file_key 59.192 + ) 59.193 + end 59.194 + local function try_dir(dirname) 59.195 + local dir = io.open(dirname) 59.196 + if dir then 59.197 + io.close(dir) 59.198 + local obj = {} 59.199 + local sub_path = {} 59.200 + for i, v in ipairs(path) do sub_path[i] = v end 59.201 + table.insert(sub_path, file_key) 59.202 + install_autoloader(obj, category, sub_path) 59.203 + rawset(self, key, obj) 59.204 + try_exec(path_string .. "/__init.lua") 59.205 + if merge then try_exec(merge_path_string .. "/__init.lua") end 59.206 + return true 59.207 + else 59.208 + return false 59.209 + end 59.210 + end 59.211 + if merge and try_exec(merge_path_string .. ".lua") then 59.212 + elseif merge and try_dir(merge_path_string .. "/") then 59.213 + elseif try_exec(path_string .. ".lua") then 59.214 + elseif try_dir(path_string .. "/") then 59.215 + else end 59.216 + return rawget(self, key) 59.217 + end 59.218 + install_autoloader(_G, nil, {}) 59.219 + try_exec("../env/__init.lua") 59.220 +end 59.221 + 59.222 +-- interactive console mode 59.223 +if not cgi then 59.224 + local config_name = request.get_config_name() 59.225 + if config_name then 59.226 + execute.config(config_name) 59.227 + end 59.228 + return 59.229 +end 59.230 + 59.231 +local success, error_info = xpcall( 59.232 + function() 59.233 + 59.234 + -- execute configuration file 59.235 + do 59.236 + local config_name = request.get_config_name() 59.237 + if config_name then 59.238 + execute.config(config_name) 59.239 + end 59.240 + end 59.241 + 59.242 + -- restore slots if coming from http redirect 59.243 + if cgi.params.tempstore then 59.244 + trace.restore_slots{} 59.245 + local blob = tempstore.pop(cgi.params.tempstore) 59.246 + if blob then slot.restore_all(blob) end 59.247 + end 59.248 + 59.249 + local function file_exists(filename) 59.250 + local file = io.open(filename, "r") 59.251 + if file then 59.252 + io.close(file) 59.253 + return true 59.254 + else 59.255 + return false 59.256 + end 59.257 + end 59.258 + 59.259 + if cgi.params["_webmcp_404"] then 59.260 + request.force_absolute_baseurl() 59.261 + request.set_status("404 Not Found") 59.262 + if request.get_404_route() then 59.263 + request.forward(request.get_404_route()) 59.264 + else 59.265 + error("No 404 page set.") 59.266 + end 59.267 + elseif request.get_action() then 59.268 + trace.request{ 59.269 + module = request.get_module(), 59.270 + action = request.get_action() 59.271 + } 59.272 + if 59.273 + request.get_404_route() and 59.274 + not file_exists( 59.275 + encode.action_file_path{ 59.276 + module = request.get_module(), 59.277 + action = request.get_action() 59.278 + } 59.279 + ) 59.280 + then 59.281 + request.set_status("404 Not Found") 59.282 + request.forward(request.get_404_route()) 59.283 + else 59.284 + if cgi.method ~= "POST" then 59.285 + request.set_status("405 Method Not Allowed") 59.286 + cgi.add_header("Allow: POST") 59.287 + error("Tried to invoke an action with a GET request.") 59.288 + end 59.289 + local action_status = execute.filtered_action{ 59.290 + module = request.get_module(), 59.291 + action = request.get_action(), 59.292 + } 59.293 + if not request.is_rerouted() then 59.294 + local routing_mode, routing_module, routing_view 59.295 + routing_mode = cgi.params["_webmcp_routing." .. action_status .. ".mode"] 59.296 + routing_module = cgi.params["_webmcp_routing." .. action_status .. ".module"] 59.297 + routing_view = cgi.params["_webmcp_routing." .. action_status .. ".view"] 59.298 + if not (routing_mode or routing_module or routing_view) then 59.299 + action_status = "default" 59.300 + routing_mode = cgi.params["_webmcp_routing.default.mode"] 59.301 + routing_module = cgi.params["_webmcp_routing.default.module"] 59.302 + routing_view = cgi.params["_webmcp_routing.default.view"] 59.303 + end 59.304 + assert(routing_module, "Routing information has no module.") 59.305 + assert(routing_view, "Routing information has no view.") 59.306 + if routing_mode == "redirect" then 59.307 + local routing_params = {} 59.308 + for key, value in pairs(cgi.params) do 59.309 + local status, stripped_key = string.match( 59.310 + key, "^_webmcp_routing%.([^%.]*)%.params%.(.*)$" 59.311 + ) 59.312 + if status == action_status then 59.313 + routing_params[stripped_key] = value 59.314 + end 59.315 + end 59.316 + request.redirect{ 59.317 + module = routing_module, 59.318 + view = routing_view, 59.319 + id = cgi.params["_webmcp_routing." .. action_status .. ".id"], 59.320 + params = routing_params 59.321 + } 59.322 + elseif routing_mode == "forward" then 59.323 + request.forward{ module = routing_module, view = routing_view } 59.324 + else 59.325 + error("Missing or unknown routing mode in request parameters.") 59.326 + end 59.327 + end 59.328 + end 59.329 + else 59.330 + -- no action 59.331 + trace.request{ 59.332 + module = request.get_module(), 59.333 + view = request.get_view() 59.334 + } 59.335 + if 59.336 + request.get_404_route() and 59.337 + not file_exists( 59.338 + encode.view_file_path{ 59.339 + module = request.get_module(), 59.340 + view = request.get_view() 59.341 + } 59.342 + ) 59.343 + then 59.344 + request.set_status("404 Not Found") 59.345 + request.forward(request.get_404_route()) 59.346 + end 59.347 + end 59.348 + 59.349 + if not request.get_redirect_data() then 59.350 + request.process_forward() 59.351 + execute.filtered_view{ 59.352 + module = request.get_module(), 59.353 + view = request.get_view(), 59.354 + } 59.355 + end 59.356 + 59.357 + -- force error due to missing absolute base URL until its too late to display error message 59.358 + --if request.get_redirect_data() then 59.359 + -- request.get_absolute_baseurl() 59.360 + --end 59.361 + 59.362 + end, 59.363 + 59.364 + function(errobj) 59.365 + return { 59.366 + errobj = errobj, 59.367 + stacktrace = string.gsub( 59.368 + debug.traceback('', 2), 59.369 + "^\r?\n?stack traceback:\r?\n?", "" 59.370 + ) 59.371 + } 59.372 + end 59.373 +) 59.374 + 59.375 +if not success then trace.error{} end 59.376 + 59.377 +-- laufzeitermittlung 59.378 +trace.exectime{ real = os.monotonic_hires_time(), cpu = os.clock() } 59.379 + 59.380 +slot.select('trace', trace.render) -- render trace information 59.381 + 59.382 +local redirect_data = request.get_redirect_data() 59.383 + 59.384 +-- log error and switch to error layout, unless success 59.385 +if not success then 59.386 + local errobj = error_info.errobj 59.387 + local stacktrace = error_info.stacktrace 59.388 + if not request.get_status() then 59.389 + request.set_status("500 Internal Server Error") 59.390 + end 59.391 + slot.set_layout('system_error') 59.392 + slot.select('system_error', function() 59.393 + if getmetatable(errobj) == mondelefant.errorobject_metatable then 59.394 + slot.put( 59.395 + "<p>Database error of class <b>", 59.396 + encode.html(errobj.code), 59.397 + "</b> occured:<br/><b>", 59.398 + encode.html(errobj.message), 59.399 + "</b></p>" 59.400 + ) 59.401 + else 59.402 + slot.put("<p><b>", encode.html(tostring(errobj)), "</b></p>") 59.403 + end 59.404 + slot.put("<p>Stack trace follows:<br/>") 59.405 + slot.put(encode.html_newlines(encode.html(stacktrace))) 59.406 + slot.put("</p>") 59.407 + end) 59.408 +elseif redirect_data then 59.409 + local redirect_params = {} 59.410 + for key, value in pairs(redirect_data.params) do 59.411 + redirect_params[key] = value 59.412 + end 59.413 + local slot_dump = slot.dump_all() 59.414 + if slot_dump ~= "" then 59.415 + redirect_params.tempstore = tempstore.save(slot_dump) 59.416 + end 59.417 + cgi.redirect( 59.418 + encode.url{ 59.419 + base = request.get_absolute_baseurl(), 59.420 + module = redirect_data.module, 59.421 + view = redirect_data.view, 59.422 + id = redirect_data.id, 59.423 + params = redirect_params 59.424 + } 59.425 + ) 59.426 + cgi.send_data() 59.427 +end 59.428 + 59.429 +if not success or not redirect_data then 59.430 + 59.431 + local http_status = request.get_status() 59.432 + if http_status then 59.433 + cgi.set_status(http_status) 59.434 + end 59.435 + 59.436 + -- ajax 59.437 + if request.is_ajax() then 59.438 + cgi.set_content_type('text/html') 59.439 + for i, slot_ident in ipairs{'main', 'actions', 'title', 'topnav', 'sidenav', 'debug', 'notice', 'warning', 'error'} do 59.440 + local html = slot.get_content(slot_ident) 59.441 + if html then 59.442 + cgi.send_data("document.getElementById('" .. slot_ident .. "').innerHTML=" .. encode.json(html or ' ') .. ";") 59.443 + end 59.444 + end 59.445 + -- oder ganz herkoemmlich 59.446 + else 59.447 + cgi.set_content_type(slot.get_content_type()) 59.448 + cgi.send_data(slot.render_layout()) 59.449 + end 59.450 +end 59.451 + 59.452 +exit()
60.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 60.2 +++ b/framework/env/__init.lua Sun Oct 25 12:00:00 2009 +0100 60.3 @@ -0,0 +1,16 @@ 60.4 +function _(text, replacements) 60.5 + local text = locale._get_translation_table()[text] or text 60.6 + if replacements then 60.7 + return ( 60.8 + string.gsub( 60.9 + text, 60.10 + "#{(.-)}", 60.11 + function (placeholder) 60.12 + return replacements[placeholder] 60.13 + end 60.14 + ) 60.15 + ) 60.16 + else 60.17 + return text 60.18 + end 60.19 +end
61.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 61.2 +++ b/framework/env/charset/__init.lua Sun Oct 25 12:00:00 2009 +0100 61.3 @@ -0,0 +1,1 @@ 61.4 +charset._current = "UTF-8"
62.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 62.2 +++ b/framework/env/charset/data/utf_8.lua Sun Oct 25 12:00:00 2009 +0100 62.3 @@ -0,0 +1,10 @@ 62.4 +charset.data.utf_8 = { 62.5 + special_chars = { 62.6 + nobreak_space = "\194\160", 62.7 + minus_sign = "\226\136\146", 62.8 + inf_sign = "\226\136\158", 62.9 + hyphen_sign = "\226\128\144", 62.10 + nobreak_hyphen = "\226\128\145", 62.11 + figure_dash = "\226\128\146", 62.12 + }, 62.13 +}
63.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 63.2 +++ b/framework/env/charset/get.lua Sun Oct 25 12:00:00 2009 +0100 63.3 @@ -0,0 +1,11 @@ 63.4 +--[[-- 63.5 +current_charset = -- currently selected character set to be used 63.6 +charset.get() 63.7 + 63.8 +Returns the currently selected character set, which is used by the application. Defaults to "UTF-8" unless being changed. 63.9 + 63.10 +--]]-- 63.11 + 63.12 +function charset.get() 63.13 + return charset._current 63.14 +end
64.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 64.2 +++ b/framework/env/charset/get_data.lua Sun Oct 25 12:00:00 2009 +0100 64.3 @@ -0,0 +1,13 @@ 64.4 +--[[-- 64.5 +charset_data = -- table containing information about the current charset 64.6 +charset.get_data() 64.7 + 64.8 +Returns a table with information about the currently selected charset. See framework/env/charset/data/ for more information. 64.9 + 64.10 +--]]-- 64.11 + 64.12 +function charset.get_data() 64.13 + return charset.data[ 64.14 + string.gsub(string.lower(charset._current), "%-", "_") 64.15 + ] 64.16 +end
65.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 65.2 +++ b/framework/env/charset/set.lua Sun Oct 25 12:00:00 2009 +0100 65.3 @@ -0,0 +1,12 @@ 65.4 +--[[-- 65.5 +charset.set( 65.6 + charset_ident -- identifier of a charset, i.e. "UTF-8" 65.7 +) 65.8 + 65.9 +Changes the currently used charset. Only "UTF-8" is supported, which is already set as a default, so calling this function is not really useful yet. 65.10 + 65.11 +--]]-- 65.12 + 65.13 +function charset.set(charset_ident) 65.14 + charset._current = charset_ident 65.15 +end
66.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 66.2 +++ b/framework/env/convert/DEPRECATED Sun Oct 25 12:00:00 2009 +0100 66.3 @@ -0,0 +1,6 @@ 66.4 +framework/env/convert and framework/env/ui_deprecated are deprecated! 66.5 + 66.6 +They will be replaced by: 66.7 +- framework/env/format 66.8 +- framework/env/parse 66.9 +- framework/env/ui
67.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 67.2 +++ b/framework/env/convert/__init.lua Sun Oct 25 12:00:00 2009 +0100 67.3 @@ -0,0 +1,10 @@ 67.4 +convert._type_symbol_mappings = setmetatable({}, { __mode = "k" }) 67.5 + 67.6 +convert.register_type(atom.boolean, "boolean") 67.7 +convert.register_type(atom.string, "string") 67.8 +convert.register_type(atom.integer, "integer") 67.9 +convert.register_type(atom.number, "number") 67.10 +convert.register_type(atom.fraction, "fraction") 67.11 +convert.register_type(atom.date, "date") 67.12 +convert.register_type(atom.timestamp, "timestamp") 67.13 +convert.register_type(atom.time, "time") 67.14 \ No newline at end of file
68.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 68.2 +++ b/framework/env/convert/_from_boolean_to_human.lua Sun Oct 25 12:00:00 2009 +0100 68.3 @@ -0,0 +1,3 @@ 68.4 +function convert._from_boolean_to_human(value) 68.5 + return atom.dump(value) 68.6 +end
69.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 69.2 +++ b/framework/env/convert/_from_date_to_human.lua Sun Oct 25 12:00:00 2009 +0100 69.3 @@ -0,0 +1,3 @@ 69.4 +function convert._from_date_to_human(value) 69.5 + return atom.dump(value) 69.6 +end
70.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 70.2 +++ b/framework/env/convert/_from_fraction_to_human.lua Sun Oct 25 12:00:00 2009 +0100 70.3 @@ -0,0 +1,3 @@ 70.4 +function convert._from_fraction_to_human(value) 70.5 + return atom.dump(value) 70.6 +end
71.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 71.2 +++ b/framework/env/convert/_from_human_to_boolean.lua Sun Oct 25 12:00:00 2009 +0100 71.3 @@ -0,0 +1,3 @@ 71.4 +function convert._from_human_to_boolean(str) 71.5 + return atom.boolean:load(str) 71.6 +end
72.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 72.2 +++ b/framework/env/convert/_from_human_to_date.lua Sun Oct 25 12:00:00 2009 +0100 72.3 @@ -0,0 +1,3 @@ 72.4 +function convert._from_human_to_date(str) 72.5 + return atom.date:load(str) 72.6 +end
73.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 73.2 +++ b/framework/env/convert/_from_human_to_fraction.lua Sun Oct 25 12:00:00 2009 +0100 73.3 @@ -0,0 +1,3 @@ 73.4 +function convert._from_human_to_fraction(str) 73.5 + return atom.fraction:load(str) 73.6 +end
74.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 74.2 +++ b/framework/env/convert/_from_human_to_integer.lua Sun Oct 25 12:00:00 2009 +0100 74.3 @@ -0,0 +1,3 @@ 74.4 +function convert._from_human_to_integer(str) 74.5 + return atom.integer:load(str) 74.6 +end
75.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 75.2 +++ b/framework/env/convert/_from_human_to_number.lua Sun Oct 25 12:00:00 2009 +0100 75.3 @@ -0,0 +1,3 @@ 75.4 +function convert._from_human_to_number(str) 75.5 + return atom.number:load(str) 75.6 +end
76.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 76.2 +++ b/framework/env/convert/_from_human_to_string.lua Sun Oct 25 12:00:00 2009 +0100 76.3 @@ -0,0 +1,3 @@ 76.4 +function convert._from_human_to_string(str) 76.5 + return atom.string:load(str) 76.6 +end
77.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 77.2 +++ b/framework/env/convert/_from_human_to_time.lua Sun Oct 25 12:00:00 2009 +0100 77.3 @@ -0,0 +1,3 @@ 77.4 +function convert._from_human_to_time(str) 77.5 + return atom.time:load(str) 77.6 +end
78.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 78.2 +++ b/framework/env/convert/_from_human_to_timestamp.lua Sun Oct 25 12:00:00 2009 +0100 78.3 @@ -0,0 +1,3 @@ 78.4 +function convert._from_human_to_timestamp(str) 78.5 + return atom.timestamp:load(str) 78.6 +end
79.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 79.2 +++ b/framework/env/convert/_from_integer_to_human.lua Sun Oct 25 12:00:00 2009 +0100 79.3 @@ -0,0 +1,3 @@ 79.4 +function convert._from_integer_to_human(value) 79.5 + return atom.dump(value) 79.6 +end
80.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 80.2 +++ b/framework/env/convert/_from_number_to_human.lua Sun Oct 25 12:00:00 2009 +0100 80.3 @@ -0,0 +1,3 @@ 80.4 +function convert._from_number_to_human(value) 80.5 + return atom.dump(value) 80.6 +end
81.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 81.2 +++ b/framework/env/convert/_from_string_to_human.lua Sun Oct 25 12:00:00 2009 +0100 81.3 @@ -0,0 +1,3 @@ 81.4 +function convert._from_string_to_human(value) 81.5 + return atom.dump(value) 81.6 +end
82.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 82.2 +++ b/framework/env/convert/_from_time_to_human.lua Sun Oct 25 12:00:00 2009 +0100 82.3 @@ -0,0 +1,3 @@ 82.4 +function convert._from_time_to_human(value) 82.5 + return atom.dump(value) 82.6 +end
83.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 83.2 +++ b/framework/env/convert/_from_timestamp_to_human.lua Sun Oct 25 12:00:00 2009 +0100 83.3 @@ -0,0 +1,3 @@ 83.4 +function convert._from_timestamp_to_human(value) 83.5 + return atom.dump(value) 83.6 +end
84.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 84.2 +++ b/framework/env/convert/from_human.lua Sun Oct 25 12:00:00 2009 +0100 84.3 @@ -0,0 +1,15 @@ 84.4 +function convert.from_human(str, typ) 84.5 + if not typ then 84.6 + error("Using convert.from_human(...) to convert a human readable string to an internal data type needs a type to be specified as second parameter.") 84.7 + end 84.8 + if not str then return nil end -- TODO: decide, if an error should be raised instead 84.9 + local type_symbol = convert._type_symbol_mappings[typ] 84.10 + if not type_symbol then 84.11 + error("Unrecognized type reference passed to convert.from_human(...).") 84.12 + end 84.13 + local converter = convert["_from_human_to_" .. type_symbol] 84.14 + if not converter then 84.15 + error("Type reference passed to convert.from_human(...) was recognized, but the converter function is not existent.") 84.16 + end 84.17 + return converter(str) 84.18 +end 84.19 \ No newline at end of file
85.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 85.2 +++ b/framework/env/convert/register_type.lua Sun Oct 25 12:00:00 2009 +0100 85.3 @@ -0,0 +1,3 @@ 85.4 +function convert.register_type(typ, name) 85.5 + convert._type_symbol_mappings[typ] = name 85.6 +end
86.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 86.2 +++ b/framework/env/convert/to_human.lua Sun Oct 25 12:00:00 2009 +0100 86.3 @@ -0,0 +1,21 @@ 86.4 +function convert.to_human(value, typ) 86.5 + if value == nil then return "" end -- TODO: is this correct? 86.6 + if typ and not atom.has_type(value, typ) then 86.7 + error("The value passed to convert.to_human(...) has not the specified type.") 86.8 + end 86.9 + local type_symbol 86.10 + local value_type = type(value) 86.11 + if value_type ~= "table" and value_type ~= "userdata" then 86.12 + type_symbol = value_type 86.13 + else 86.14 + type_symbol = convert._type_symbol_mappings[getmetatable(value)] 86.15 + end 86.16 + if not type_symbol then 86.17 + error("Unrecognized type reference occurred in convert.to_human(...).") 86.18 + end 86.19 + local converter = convert["_from_" .. type_symbol .. "_to_human"] 86.20 + if not converter then 86.21 + error("Type reference in convert.from_human(...) could be recognized, but the converter function is not existent.") 86.22 + end 86.23 + return converter(value) 86.24 +end
87.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 87.2 +++ b/framework/env/encode/action_file_path.lua Sun Oct 25 12:00:00 2009 +0100 87.3 @@ -0,0 +1,21 @@ 87.4 +--[[-- 87.5 +path = -- string containing a path to an action 87.6 +encode.action_file_path{ 87.7 + module = module, -- module name 87.8 + action = action -- action name 87.9 +} 87.10 + 87.11 +This function returns the file path of an action with a given module name and action name. Both module name and action name are mandatory arguments. 87.12 + 87.13 +--]]-- 87.14 + 87.15 +function encode.action_file_path(args) 87.16 + return (encode.file_path( 87.17 + request.get_app_basepath(), 87.18 + 'app', 87.19 + request.get_app_name(), 87.20 + args.module, 87.21 + '_action', 87.22 + args.action .. '.lua' 87.23 + )) 87.24 +end
88.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 88.2 +++ b/framework/env/encode/concat_file_path.lua Sun Oct 25 12:00:00 2009 +0100 88.3 @@ -0,0 +1,19 @@ 88.4 +--[[-- 88.5 +path = -- string containing a (file) path 88.6 +encode.concat_file_path( 88.7 + element1, -- first part of the path 88.8 + element2, -- second part of the path 88.9 + ... -- more parts of the path 88.10 +) 88.11 + 88.12 +This function takes a variable amount of strings as arguments and returns a concatenation with slashes as seperators. Multiple slashes following each other directly are transformed into a single slash. 88.13 + 88.14 +--]]-- 88.15 + 88.16 +function encode.concat_file_path(...) 88.17 + return ( 88.18 + string.gsub( 88.19 + table.concat({...}, "/"), "/+", "/" 88.20 + ) 88.21 + ) 88.22 +end
89.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 89.2 +++ b/framework/env/encode/file_path.lua Sun Oct 25 12:00:00 2009 +0100 89.3 @@ -0,0 +1,21 @@ 89.4 +--[[-- 89.5 +path = -- string containing a (file) path 89.6 +encode.encode_file_path( 89.7 + base_path, 89.8 + element1, -- next part of the path 89.9 + element2, -- next part of the path 89.10 + ... 89.11 +) 89.12 + 89.13 +This function does the same as encode.concat_file_path, except that all arguments but the first are encoded using the encode.file_path_element function. 89.14 + 89.15 +--]]-- 89.16 + 89.17 +function encode.file_path(base, ...) -- base argument is not encoded 89.18 + local raw_elements = {...} 89.19 + local encoded_elements = {} 89.20 + for i = 1, #raw_elements do 89.21 + encoded_elements[i] = encode.file_path_element(raw_elements[i]) 89.22 + end 89.23 + return encode.concat_file_path(base, unpack(encoded_elements)) 89.24 +end
90.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 90.2 +++ b/framework/env/encode/file_path_element.lua Sun Oct 25 12:00:00 2009 +0100 90.3 @@ -0,0 +1,22 @@ 90.4 +--[[-- 90.5 +encoded_path_element = -- string which can't contain evil stuff like "/" 90.6 +encode.file_path_element( 90.7 + path_element -- string to be encoded 90.8 +) 90.9 + 90.10 +This function is encoding a string in a way that it can be used as a file or directory name, without security risks. See the source for details. 90.11 + 90.12 +--]]-- 90.13 + 90.14 +function encode.file_path_element(path_element) 90.15 + return ( 90.16 + string.gsub( 90.17 + string.gsub( 90.18 + path_element, "[^0-9A-Za-z_%.-]", 90.19 + function(char) 90.20 + return string.format("%%%02x", string.byte(char)) 90.21 + end 90.22 + ), "^%.", string.format("%%%%%02x", string.byte(".")) 90.23 + ) 90.24 + ) 90.25 +end
91.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 91.2 +++ b/framework/env/encode/format_info.lua Sun Oct 25 12:00:00 2009 +0100 91.3 @@ -0,0 +1,14 @@ 91.4 +--[[-- 91.5 +string = -- string to be used as __format information 91.6 +encode.format_info( 91.7 + format, -- name of format function 91.8 + params -- arguments for format function 91.9 +) 91.10 + 91.11 +The string returned by the function can be used as value in a hidden form field with a "__format" suffix. It will be used by the param.* functions to parse a string. 91.12 + 91.13 +--]]-- 91.14 + 91.15 +function encode.format_info(format, params) 91.16 + return format .. encode.format_options(params) 91.17 +end
92.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 92.2 +++ b/framework/env/encode/format_options.lua Sun Oct 25 12:00:00 2009 +0100 92.3 @@ -0,0 +1,40 @@ 92.4 +--[[-- 92.5 +string = -- part of string to be used as __format information 92.6 +encode.format_options( 92.7 + params -- arguments for format function 92.8 +) 92.9 + 92.10 +This function is used by encode.format_info(...). 92.11 + 92.12 +--]]-- 92.13 + 92.14 +function encode.format_options(params) 92.15 + local params = params or {} 92.16 + local result_parts = {} 92.17 + for key, value in pairs(params) do 92.18 + if type(key) == "string" then 92.19 + if string.find(key, "^[A-Za-z][A-Za-z0-9_]*$") then 92.20 + table.insert(result_parts, "-") 92.21 + table.insert(result_parts, key) 92.22 + table.insert(result_parts, "-") 92.23 + local t = type(value) 92.24 + if t == "string" then 92.25 + value = string.gsub(value, "\\", "\\\\") 92.26 + value = string.gsub(value, "'", "\\'") 92.27 + table.insert(result_parts, "'") 92.28 + table.insert(result_parts, value) 92.29 + table.insert(result_parts, "'") 92.30 + elseif t == "number" then 92.31 + table.insert(result_parts, tostring(value)) 92.32 + elseif t == "boolean" then 92.33 + table.insert(result_parts, value and "true" or "false") 92.34 + else 92.35 + error("Format parameter table contained value of unsupported type " .. t .. ".") 92.36 + end 92.37 + else 92.38 + error('Format parameter table contained invalid key "' .. key .. '".') 92.39 + end 92.40 + end 92.41 + end 92.42 + return table.concat(result_parts) 92.43 +end
93.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 93.2 +++ b/framework/env/encode/html.lua Sun Oct 25 12:00:00 2009 +0100 93.3 @@ -0,0 +1,32 @@ 93.4 +--[[-- 93.5 +result = -- encoded string 93.6 +encode.html( 93.7 + str -- original string 93.8 +) 93.9 + 93.10 +This function replaces the special characters '<', '>', '&' and '"' by their HTML entities '<', '&rt;', '&' and '"'. 93.11 + 93.12 +NOTE: ACCELERATED FUNCTION 93.13 +Do not change unless also you also update webmcp_accelerator.c 93.14 + 93.15 +--]]-- 93.16 + 93.17 +function encode.html(text) 93.18 + -- TODO: perhaps filter evil control characters? 93.19 + return ( 93.20 + string.gsub( 93.21 + text, '[<>&"]', 93.22 + function(char) 93.23 + if char == '<' then 93.24 + return "<" 93.25 + elseif char == '>' then 93.26 + return ">" 93.27 + elseif char == '&' then 93.28 + return "&" 93.29 + elseif char == '"' then 93.30 + return """ 93.31 + end 93.32 + end 93.33 + ) 93.34 + ) 93.35 +end
94.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 94.2 +++ b/framework/env/encode/html_newlines.lua Sun Oct 25 12:00:00 2009 +0100 94.3 @@ -0,0 +1,13 @@ 94.4 +--[[-- 94.5 +text_with_br_tags = -- text with <br/> tags 94.6 +encode.html_newlines( 94.7 + text_with_lf_control_characters -- text with LF control characters 94.8 +) 94.9 + 94.10 +This function transforms LF control characters (\n) into <br/> tags. 94.11 + 94.12 +--]]-- 94.13 + 94.14 +function encode.html_newlines(text) 94.15 + return (string.gsub(text, '\n', '<br/>')) 94.16 +end
95.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 95.2 +++ b/framework/env/encode/json.lua Sun Oct 25 12:00:00 2009 +0100 95.3 @@ -0,0 +1,34 @@ 95.4 +--[[-- 95.5 +json_string = -- JavaScript code representing the given datum (with quotes, if needed) 95.6 +encode.json( 95.7 + obj -- true, false, nil or a number or string 95.8 +) 95.9 + 95.10 +This function encodes any native datatype or atom in JavaScript object notation (JSON). 95.11 + 95.12 +--]]-- 95.13 + 95.14 +function encode.json(obj) 95.15 + if obj == nil then 95.16 + return "null"; 95.17 + elseif atom.has_type(obj, atom.boolean) then 95.18 + return tostring(obj) 95.19 + elseif atom.has_type(obj, atom.number) then 95.20 + return tostring(obj) 95.21 + else 95.22 + return 95.23 + "'" .. 95.24 + string.gsub(atom.dump(obj), ".", 95.25 + function (char) 95.26 + if char == "\r" then return "\\r" end 95.27 + if char == "\n" then return "\\n" end 95.28 + if char == "\\" then return "\\\\" end 95.29 + if char == "'" then return "\\'" end 95.30 + if char == "/" then return "\\/" end -- allowed according to RFC4627, needed for </script> 95.31 + local byte = string.byte(char) 95.32 + if byte < 32 then return string.format("\\u%04x", byte) end 95.33 + end 95.34 + ) .. 95.35 + "'" 95.36 + end 95.37 +end
96.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 96.2 +++ b/framework/env/encode/mime/atom_token.lua Sun Oct 25 12:00:00 2009 +0100 96.3 @@ -0,0 +1,41 @@ 96.4 +function encode.mime.atom_token(str) 96.5 + local charset = "UTF-8" -- TODO: support other charsets via locale system 96.6 + if 96.7 + string.find(str, "^[0-9A-Za-z!#%$%%&'%*%+%-/=%?%^_`{|}~]+$") 96.8 + then 96.9 + return str 96.10 + elseif 96.11 + string.find(str, "^[\t 0-9A-Za-z!#%$%%&'%*%+%-/=%?%^_`{|}~]+$") 96.12 + then 96.13 + return '"' .. str .. '"' 96.14 + elseif 96.15 + string.find(str, "^[\t -~]*$") 96.16 + then 96.17 + local parts = { '"' } 96.18 + for char in string.gmatch(str, ".") do 96.19 + if char == '"' or char == "\\" then 96.20 + parts[#parts+1] = "\\" 96.21 + end 96.22 + parts[#parts+1] = char 96.23 + end 96.24 + parts[#parts+1] = '"' 96.25 + return table.concat(parts) 96.26 + else 96.27 + local parts = { "=?", charset, "?Q?" } 96.28 + for char in string.gmatch(str, ".") do 96.29 + local byte = string.byte(char) 96.30 + if string.find(char, "^[0-9A-Za-z%.%-]$") then 96.31 + parts[#parts+1] = char 96.32 + else 96.33 + local byte = string.byte(char) 96.34 + if byte == 32 then 96.35 + parts[#parts+1] = "_" 96.36 + else 96.37 + parts[#parts+1] = string.format("=%02X", byte) 96.38 + end 96.39 + end 96.40 + end 96.41 + parts[#parts+1] = "?=" 96.42 + return table.concat(parts) 96.43 + end 96.44 +end 96.45 \ No newline at end of file
97.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 97.2 +++ b/framework/env/encode/mime/base64.lua Sun Oct 25 12:00:00 2009 +0100 97.3 @@ -0,0 +1,45 @@ 97.4 +local alphabet = { 97.5 + "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", 97.6 + "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", 97.7 + "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", 97.8 + "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", 97.9 + "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "+", "/" 97.10 +} 97.11 + 97.12 +function encode.mime.base64(str) 97.13 + local parts = {} 97.14 + local pos = 1 97.15 + local block_count = 0 97.16 + while pos <= #str do 97.17 + local s = string.sub(str, pos, pos + 2) 97.18 + local n = 0 97.19 + for i = 1, 3 do 97.20 + n = n * 256 97.21 + if i <= #s then 97.22 + n = n + string.byte(string.sub(s, i, i)) 97.23 + end 97.24 + end 97.25 + parts[#parts+1] = alphabet[math.floor(n / 262144) + 1] 97.26 + parts[#parts+1] = alphabet[math.floor(n / 4096) % 64 + 1] 97.27 + if #s > 1 then 97.28 + parts[#parts+1] = alphabet[math.floor(n / 64) % 64 + 1] 97.29 + else 97.30 + parts[#parts+1] = "=" 97.31 + end 97.32 + if #s > 2 then 97.33 + parts[#parts+1] = alphabet[n % 64 + 1] 97.34 + else 97.35 + parts[#parts+1] = "=" 97.36 + end 97.37 + block_count = block_count + 1 97.38 + if block_count == 19 then 97.39 + parts[#parts+1] = "\r\n" 97.40 + block_count = 0 97.41 + end 97.42 + pos = pos + #s 97.43 + end 97.44 + if block_count > 0 then 97.45 + parts[#parts+1] = "\r\n" 97.46 + end 97.47 + return table.concat(parts) 97.48 +end
98.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 98.2 +++ b/framework/env/encode/mime/mail.lua Sun Oct 25 12:00:00 2009 +0100 98.3 @@ -0,0 +1,66 @@ 98.4 +local un = encode.mime.unstructured_header_line 98.5 +local mbl = encode.mime.mailbox_list_header_line 98.6 + 98.7 +local function encode_container(parts, container) 98.8 + if container.multipart then 98.9 + local boundary 98.10 + boundary = "BOUNDARY--" .. multirand.string( 98.11 + 24, 98.12 + "123456789BCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz" 98.13 + ) 98.14 + parts[#parts+1] = "Content-Type: " 98.15 + parts[#parts+1] = "multipart/" 98.16 + parts[#parts+1] = container.multipart 98.17 + parts[#parts+1] = "; boundary=" 98.18 + parts[#parts+1] = boundary 98.19 + parts[#parts+1] = "\r\n\r\nMIME multipart\r\n" -- last \r\n optional 98.20 + for idx, sub_container in ipairs(container) do 98.21 + parts[#parts+1] = "\r\n--" 98.22 + parts[#parts+1] = boundary 98.23 + parts[#parts+1] = "\r\n" 98.24 + encode_container(parts, sub_container) 98.25 + end 98.26 + parts[#parts+1] = "\r\n--" 98.27 + parts[#parts+1] = boundary 98.28 + parts[#parts+1] = "--\r\n" 98.29 + else 98.30 + parts[#parts+1] = "Content-Type: " 98.31 + parts[#parts+1] = container.content_type or "text/plain" 98.32 + parts[#parts+1] = "\r\n" 98.33 + if container.content_id then 98.34 + parts[#parts+1] = "Content-ID: <" 98.35 + parts[#parts+1] = container.content_id 98.36 + parts[#parts+1] = ">\r\n" 98.37 + end 98.38 + if container.attachment_filename then 98.39 + parts[#parts+1] = "Content-Disposition: attachment; filename=" 98.40 + parts[#parts+1] = encode.mime.atom_token( 98.41 + container.attachment_filename 98.42 + ) 98.43 + parts[#parts+1] = "\r\n" 98.44 + end 98.45 + if container.binary then 98.46 + parts[#parts+1] = "Content-Transfer-Encoding: base64\r\n\r\n" 98.47 + parts[#parts+1] = encode.mime.base64(container.content) 98.48 + else 98.49 + parts[#parts+1] = 98.50 + "Content-Transfer-Encoding: quoted-printable\r\n\r\n" 98.51 + parts[#parts+1] = 98.52 + encode.mime.quoted_printable_text_content(container.content) 98.53 + end 98.54 + end 98.55 +end 98.56 + 98.57 +function encode.mime.mail(args) 98.58 + local parts = {} 98.59 + parts[#parts+1] = mbl("From", args.from) 98.60 + parts[#parts+1] = mbl("Sender", args.sender) 98.61 + parts[#parts+1] = mbl("Reply-To", args.reply_to) 98.62 + parts[#parts+1] = mbl("To", args.to) 98.63 + parts[#parts+1] = mbl("Cc", args.cc) 98.64 + parts[#parts+1] = mbl("Bcc", args.bcc) 98.65 + parts[#parts+1] = un("Subject", args.subject) 98.66 + parts[#parts+1] = "MIME-Version: 1.0\r\n" 98.67 + encode_container(parts, args) 98.68 + return table.concat(parts) 98.69 +end
99.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 99.2 +++ b/framework/env/encode/mime/mailbox_list_header_line.lua Sun Oct 25 12:00:00 2009 +0100 99.3 @@ -0,0 +1,58 @@ 99.4 +function encode.mime.mailbox_list_header_line(key, mailboxes) 99.5 + local mailboxes = mailboxes 99.6 + if not mailboxes then 99.7 + mailboxes = {} 99.8 + elseif type(mailboxes) == "string" then 99.9 + mailboxes = { mailboxes } 99.10 + elseif mailboxes.address or mailboxes.name then 99.11 + table.insert(mailboxes, mailboxes) 99.12 + end 99.13 + local indentation = "" 99.14 + for i = 1, #key + #(": ") do 99.15 + indentation = indentation .. " " 99.16 + end 99.17 + local parts = { key, ": " } 99.18 + local first = true 99.19 + for idx, mailbox in ipairs(mailboxes) do 99.20 + local name, address 99.21 + if type(mailbox) == "string" then 99.22 + name, address = nil, mailbox 99.23 + else 99.24 + name, address = mailbox.name, mailbox.address 99.25 + end 99.26 + if address and not string.find( 99.27 + address, 99.28 + "^[0-9A-Za-z!#%$%%&'%*%+%-/=%?%^_`{|}~%.]+" .. 99.29 + "@[0-9A-Za-z!#%$%%&'%*%+%-/=%?%^_`{|}~%.]+$" 99.30 + ) 99.31 + then 99.32 + if name then 99.33 + name, address = name .. " <" .. address .. ">", nil 99.34 + else 99.35 + name, address = address, nil 99.36 + end 99.37 + end 99.38 + if name or address then 99.39 + if not first then 99.40 + parts[#parts+1] = ",\r\n" 99.41 + parts[#parts+1] = indentation 99.42 + end 99.43 + if name then 99.44 + parts[#parts+1] = encode.mime.atom_token(name) 99.45 + parts[#parts+1] = " <" 99.46 + if address then 99.47 + parts[#parts+1] = address 99.48 + end 99.49 + parts[#parts+1] = ">" 99.50 + else 99.51 + parts[#parts+1] = address 99.52 + end 99.53 + first = false 99.54 + end 99.55 + end 99.56 + if first then 99.57 + return "" 99.58 + end 99.59 + parts[#parts+1] = "\r\n" 99.60 + return table.concat(parts) 99.61 +end
100.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 100.2 +++ b/framework/env/encode/mime/quoted_printable_text_content.lua Sun Oct 25 12:00:00 2009 +0100 100.3 @@ -0,0 +1,31 @@ 100.4 +function encode.mime.quoted_printable_text_content(str) 100.5 + local parts = {} 100.6 + for str_part in string.gmatch(str, "[^\r\n]+[\r\n]*") do 100.7 + local line, extra_gap = string.match(str_part, "^([^\r\n]+)\r?\n?(.*)$") 100.8 + local line_length = 0 100.9 + for char in string.gmatch(line, ".") do 100.10 + if string.find(char, "^[\t -<>-~]$") then 100.11 + if line_length + 1 > 75 then 100.12 + parts[#parts+1] = "=\r\n" 100.13 + line_length = 0 100.14 + end 100.15 + parts[#parts+1] = char 100.16 + line_length = line_length + 1 100.17 + else 100.18 + if line_length + 3 > 75 then 100.19 + parts[#parts+1] = "=\r\n" 100.20 + line_length = 0 100.21 + end 100.22 + parts[#parts+1] = string.format("=%02X", string.byte(char)) 100.23 + line_length = line_length + 3 100.24 + end 100.25 + end 100.26 + for i = 1, #extra_gap do 100.27 + if string.sub(extra_gap, i, i) == "\n" then 100.28 + parts[#parts+1] = "\r\n" 100.29 + end 100.30 + end 100.31 + parts[#parts+1] = "\r\n" 100.32 + end 100.33 + return table.concat(parts) 100.34 +end
101.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 101.2 +++ b/framework/env/encode/mime/unstructured_header_line.lua Sun Oct 25 12:00:00 2009 +0100 101.3 @@ -0,0 +1,71 @@ 101.4 +function encode.mime.unstructured_header_line(key, value) 101.5 + if not value then 101.6 + return "" 101.7 + end 101.8 + local charset = "UTF-8" -- TODO: support other charsets 101.9 + local key_length = #key + #(": ") 101.10 + if string.find(value, "^[\t -~]*$") then 101.11 + local need_encoding = false 101.12 + local parts = { key, ": " } 101.13 + local line_length = key_length 101.14 + local first_line = true 101.15 + for spaced_word in string.gmatch(value, "[\t ]*[^\t ]*") do 101.16 + if #spaced_word + line_length > 76 then 101.17 + if first_line or #spaced_word > 76 then 101.18 + need_encoding = true 101.19 + break 101.20 + end 101.21 + parts[#parts+1] = "\r\n" 101.22 + line_length = 0 101.23 + end 101.24 + parts[#parts+1] = spaced_word 101.25 + line_length = line_length + #spaced_word 101.26 + first_line = false 101.27 + end 101.28 + if not need_encoding then 101.29 + parts[#parts+1] = "\r\n" 101.30 + return table.concat(parts) 101.31 + end 101.32 + charset = "US-ASCII" 101.33 + end 101.34 + local parts = { key, ": " } 101.35 + local line_length 101.36 + local opening = "=?" .. charset .. "?Q?" 101.37 + local closing = "?=" 101.38 + local indentation = "" 101.39 + for i = 1, key_length do 101.40 + indentation = indentation .. " " 101.41 + end 101.42 + local open = false 101.43 + for char in string.gmatch(value, ".") do 101.44 + local encoded_char 101.45 + if string.find(char, "^[0-9A-Za-z%.%-]$") then 101.46 + encoded_char = char 101.47 + else 101.48 + local byte = string.byte(char) 101.49 + if byte == 32 then 101.50 + encoded_char = "_" 101.51 + else 101.52 + encoded_char = string.format("=%02X", byte) 101.53 + end 101.54 + end 101.55 + if open and line_length + #encoded_char > 76 then 101.56 + parts[#parts+1] = closing 101.57 + parts[#parts+1] = "\r\n" 101.58 + parts[#parts+1] = indentation 101.59 + open = false 101.60 + end 101.61 + if not open then 101.62 + parts[#parts+1] = opening 101.63 + line_length = key_length + #opening + #closing 101.64 + open = true 101.65 + end 101.66 + parts[#parts+1] = encoded_char 101.67 + line_length = line_length + #encoded_char 101.68 + end 101.69 + if open then 101.70 + parts[#parts+1] = "?=" 101.71 + end 101.72 + parts[#parts+1] = "\r\n" 101.73 + return table.concat(parts) 101.74 +end
102.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 102.2 +++ b/framework/env/encode/url.lua Sun Oct 25 12:00:00 2009 +0100 102.3 @@ -0,0 +1,89 @@ 102.4 +--[[-- 102.5 +url_string = -- a string containing an URL 102.6 +encode.url{ 102.7 + external = external, -- external URL (instead of specifying base, module, etc. below) 102.8 + base = base, -- optional string containing a base URL of a WebMCP application 102.9 + static = static, -- an URL relative to the static file directory 102.10 + module = module, -- a module name of the WebMCP application 102.11 + view = view, -- a view name of the WebMCP application 102.12 + action = action, -- an action name of the WebMCP application 102.13 + id = id, -- optional id to be passed to the view or action to select a particular data record 102.14 + params = params -- optional parameters to be passed to the view or action 102.15 +} 102.16 + 102.17 +This function creates URLs to external locations, to static files within the WebMCP application or to a certain view or action inside a module. 102.18 + 102.19 +--]]-- 102.20 + 102.21 +function encode.url(args) 102.22 + local external = args.external 102.23 + local base = args.base or request.get_relative_baseurl() 102.24 + local static = args.static 102.25 + local module = args.module 102.26 + local view = args.view 102.27 + local action = args.action 102.28 + local id = args.id 102.29 + local params = args.params or {} 102.30 + local result = {} 102.31 + local id_as_param = false 102.32 + local function add(...) 102.33 + for i = 1, math.huge do 102.34 + local v = select(i, ...) 102.35 + if v == nil then break end 102.36 + result[#result+1] = v 102.37 + end 102.38 + end 102.39 + if external then 102.40 + add(external) 102.41 + else 102.42 + add(base) 102.43 + if not string.find(base, "/$") then 102.44 + add("/") 102.45 + end 102.46 + if static then 102.47 + add("static/") 102.48 + add(static) 102.49 + elseif module or view or action or id then 102.50 + assert(module, "Module not specified.") 102.51 + add(encode.url_part(module), "/") 102.52 + if view and not action then 102.53 + local view_base, view_suffix = string.match( 102.54 + view, 102.55 + "^([^.]*)(.*)$" 102.56 + ) 102.57 + add(encode.url_part(view_base)) 102.58 + if args.id then 102.59 + add("/", encode.url_part(id)) 102.60 + end 102.61 + if view_suffix == "" then 102.62 + add(".html") 102.63 + else 102.64 + add(view_suffix) -- view_suffix includes dot as first character 102.65 + end 102.66 + elseif action and not view then 102.67 + add(encode.url_part(action)) 102.68 + id_as_param = true 102.69 + elseif view and action then 102.70 + error("Both a view and an action was specified.") 102.71 + end 102.72 + end 102.73 + do 102.74 + local new_params = request.get_perm_params() 102.75 + for key, value in pairs(params) do 102.76 + new_params[key] = value 102.77 + end 102.78 + params = new_params 102.79 + end 102.80 + end 102.81 + if next(params) ~= nil or (id and id_as_param) then 102.82 + add("?") 102.83 + if id and id_as_param then 102.84 + add("_webmcp_id=", encode.url_part(id), "&") 102.85 + end 102.86 + for key, value in pairs(params) do 102.87 + add(encode.url_part(key), "=", encode.url_part(value), "&") 102.88 + end 102.89 + result[#result] = nil -- remove last '&' or '?' 102.90 + end 102.91 + return table.concat(result) 102.92 +end
103.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 103.2 +++ b/framework/env/encode/url_part.lua Sun Oct 25 12:00:00 2009 +0100 103.3 @@ -0,0 +1,21 @@ 103.4 +--[[-- 103.5 +url_encoded_string = -- URL-encoded string 103.6 +encode.url_part( 103.7 + obj -- any native datatype or atom 103.8 +) 103.9 + 103.10 +This function encodes any native datatype or atom in a way that it can be placed inside an URL. It is first dumped with atom.dump(...) and then url-encoded. 103.11 + 103.12 +--]]-- 103.13 + 103.14 +function encode.url_part(obj) 103.15 + return ( 103.16 + string.gsub( 103.17 + atom.dump(obj), 103.18 + "[^0-9A-Za-z_%.~-]", 103.19 + function (char) 103.20 + return string.format("%%%02x", string.byte(char)) 103.21 + end 103.22 + ) 103.23 + ) 103.24 +end
104.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 104.2 +++ b/framework/env/encode/view_file_path.lua Sun Oct 25 12:00:00 2009 +0100 104.3 @@ -0,0 +1,17 @@ 104.4 +--[[-- 104.5 +path = -- string containing a path to a view 104.6 +encode.view_file_path{ 104.7 + module = module, -- module name 104.8 + view = view -- view name 104.9 +} 104.10 + 104.11 +This function returns the file path of a view with a given module name and view name. Both module name and view name are mandatory arguments. 104.12 + 104.13 +--]]-- 104.14 + 104.15 +function encode.view_file_path(args) 104.16 + return (encode.file_path( 104.17 + request.get_app_basepath(), 104.18 + 'app', request.get_app_name(), args.module, args.view .. '.lua' 104.19 + )) 104.20 +end
105.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 105.2 +++ b/framework/env/execute/__init.lua Sun Oct 25 12:00:00 2009 +0100 105.3 @@ -0,0 +1,1 @@ 105.4 +execute._wrap_stack = {}
106.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 106.2 +++ b/framework/env/execute/_add_filters_by_path.lua Sun Oct 25 12:00:00 2009 +0100 106.3 @@ -0,0 +1,25 @@ 106.4 +function execute._add_filters_by_path(filter_list, ...) 106.5 + local full_path = encode.file_path(request.get_app_basepath(), "app", ...) 106.6 + local relative_path = encode.file_path("", ...) 106.7 + local filter_names = os.listdir(full_path) 106.8 + if filter_names then 106.9 + table.sort(filter_names) -- not really neccessary, due to sorting afterwards 106.10 + for i, filter_name in ipairs(filter_names) do 106.11 + if string.find(filter_name, "%.lua$") then 106.12 + if filter_list[filter_name] then 106.13 + error('More than one filter is named "' .. filter_name .. '".') 106.14 + end 106.15 + table.insert(filter_list, filter_name) 106.16 + filter_list[filter_name] = function() 106.17 + trace.enter_filter{ 106.18 + path = encode.file_path(relative_path, filter_name) 106.19 + } 106.20 + execute.file_path{ 106.21 + file_path = encode.file_path(full_path, filter_name) 106.22 + } 106.23 + trace.execution_return() 106.24 + end 106.25 + end 106.26 + end 106.27 + end 106.28 +end
107.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 107.2 +++ b/framework/env/execute/action.lua Sun Oct 25 12:00:00 2009 +0100 107.3 @@ -0,0 +1,29 @@ 107.4 +--[[-- 107.5 +action_status = -- status code returned by the action (a string) 107.6 +execute.action{ 107.7 + module = module, -- module name of the action to be executed 107.8 + action = action, -- name of the action to be executed 107.9 + id = id, -- id to be returned by param.get_id(...) during execution 107.10 + params = params -- parameters to be returned by param.get(...) during execution 107.11 +} 107.12 + 107.13 +Executes an action without associated filters. 107.14 +This function is only used by execute.filtered_action{...}, which itself is only used by the webmcp.lua file in the cgi-bin/ directory. 107.15 + 107.16 +--]]-- 107.17 + 107.18 +function execute.action(args) 107.19 + local module = args.module 107.20 + local action = args.action 107.21 + trace.enter_action{ module = module, action = action } 107.22 + local action_status = execute.file_path{ 107.23 + file_path = encode.file_path( 107.24 + request.get_app_basepath(), 107.25 + 'app', request.get_app_name(), module, '_action', action .. '.lua' 107.26 + ), 107.27 + id = args.id, 107.28 + params = args.params 107.29 + } 107.30 + trace.execution_return{ status = action_status } 107.31 + return action_status 107.32 +end
108.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 108.2 +++ b/framework/env/execute/config.lua Sun Oct 25 12:00:00 2009 +0100 108.3 @@ -0,0 +1,19 @@ 108.4 +--[[-- 108.5 +execute.config( 108.6 + name -- name of the configuration to be loaded 108.7 +) 108.8 + 108.9 +Executes a configuration file of the application. 108.10 +This function is only used by by the webmcp.lua file in the cgi-bin/ directory. 108.11 + 108.12 +--]]-- 108.13 + 108.14 +function execute.config(name) 108.15 + trace.enter_config{ name = name } 108.16 + execute.file_path{ 108.17 + file_path = encode.file_path( 108.18 + request.get_app_basepath(), 'config', name .. '.lua' 108.19 + ) 108.20 + } 108.21 + trace.execution_return() 108.22 +end
109.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 109.2 +++ b/framework/env/execute/file_path.lua Sun Oct 25 12:00:00 2009 +0100 109.3 @@ -0,0 +1,36 @@ 109.4 +--[[-- 109.5 +status_code = -- status code returned by the executed lua file (a string) 109.6 +execute.file_path{ 109.7 + file_path = file_path, -- path to a lua source or byte-code file 109.8 + id = id, -- id to be returned by param.get_id(...) during execution 109.9 + params = params -- parameters to be returned by param.get(...) during execution 109.10 +} 109.11 + 109.12 +This function loads and executes a lua file specified by a given path. If an "id" or "params" are provided, the param.get_id(...) and/or param.get(...) functions will return the provided values during execution. The lua routine must return true, false, nil or a string. In case of true or nil, this function returns the string "ok", in case of false, this function returns "error", otherwise the string returned by the lua routine will be returned by this function as well. 109.13 + 109.14 +--]]-- 109.15 + 109.16 +function execute.file_path(args) 109.17 + local file_path = args.file_path 109.18 + local id = args.id 109.19 + local params = args.params 109.20 + local func, load_errmsg = loadfile(file_path) 109.21 + if not func then 109.22 + error('Could not load file "' .. file_path .. '": ' .. load_errmsg) 109.23 + end 109.24 + if id or params then 109.25 + param.exchange(id, params) 109.26 + end 109.27 + local result = func() 109.28 + if result == nil or result == true then 109.29 + result = 'ok' 109.30 + elseif result == false then 109.31 + result = 'error' 109.32 + elseif type(result) ~= "string" then 109.33 + error("Unexpected type of result: " .. type(result)) 109.34 + end 109.35 + if id or params then 109.36 + param.restore() 109.37 + end 109.38 + return result 109.39 +end
110.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 110.2 +++ b/framework/env/execute/filtered_action.lua Sun Oct 25 12:00:00 2009 +0100 110.3 @@ -0,0 +1,39 @@ 110.4 +--[[-- 110.5 +action_status = -- status code returned by the action (a string) 110.6 +execute.filtered_action{ 110.7 + module = module, -- module name of the action to be executed 110.8 + action = action, -- name of the action to be executed 110.9 + id = id, -- id to be returned by param.get_id(...) during execution 110.10 + params = params -- parameters to be returned by param.get(...) during execution 110.11 +} 110.12 + 110.13 +Executes an action with associated filters. 110.14 +This function is only used by by the webmcp.lua file in the cgi-bin/ directory. 110.15 + 110.16 +--]]-- 110.17 + 110.18 +function execute.filtered_action(args) 110.19 + local filters = {} 110.20 + local function add_by_path(...) 110.21 + execute._add_filters_by_path(filters, ...) 110.22 + end 110.23 + add_by_path("_filter") 110.24 + add_by_path("_filter_action") 110.25 + add_by_path(request.get_app_name(), "_filter") 110.26 + add_by_path(request.get_app_name(), "_filter_action") 110.27 + add_by_path(request.get_app_name(), args.module, "_filter") 110.28 + add_by_path(request.get_app_name(), args.module, "_filter_action") 110.29 + table.sort(filters) 110.30 + for idx, filter_name in ipairs(filters) do 110.31 + filters[idx] = filters[filter_name] 110.32 + filters[filter_name] = nil 110.33 + end 110.34 + local result 110.35 + execute.multi_wrapped( 110.36 + filters, 110.37 + function() 110.38 + result = execute.action(args) 110.39 + end 110.40 + ) 110.41 + return result 110.42 +end
111.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 111.2 +++ b/framework/env/execute/filtered_view.lua Sun Oct 25 12:00:00 2009 +0100 111.3 @@ -0,0 +1,34 @@ 111.4 +--[[-- 111.5 +execute.filtered_view{ 111.6 + module = module, -- module name of the view to be executed 111.7 + view = view -- name of the view to be executed 111.8 +} 111.9 + 111.10 +Executes a view with associated filters. 111.11 +This function is only used by by the webmcp.lua file in the cgi-bin/ directory. 111.12 + 111.13 +--]]-- 111.14 + 111.15 +function execute.filtered_view(args) 111.16 + local filters = {} 111.17 + local function add_by_path(...) 111.18 + execute._add_filters_by_path(filters, ...) 111.19 + end 111.20 + add_by_path("_filter") 111.21 + add_by_path("_filter_view") 111.22 + add_by_path(request.get_app_name(), "_filter") 111.23 + add_by_path(request.get_app_name(), "_filter_view") 111.24 + add_by_path(request.get_app_name(), args.module, "_filter") 111.25 + add_by_path(request.get_app_name(), args.module, "_filter_view") 111.26 + table.sort(filters) 111.27 + for idx, filter_name in ipairs(filters) do 111.28 + filters[idx] = filters[filter_name] 111.29 + filters[filter_name] = nil 111.30 + end 111.31 + execute.multi_wrapped( 111.32 + filters, 111.33 + function() 111.34 + execute.view(args) 111.35 + end 111.36 + ) 111.37 +end
112.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 112.2 +++ b/framework/env/execute/inner.lua Sun Oct 25 12:00:00 2009 +0100 112.3 @@ -0,0 +1,20 @@ 112.4 +--[[-- 112.5 +execute.inner() 112.6 + 112.7 +It is MANDATORY to call this function once in each filter of a WebMCP application. Calling execute.inner() calls the next filter in the filter chain, or the view or action, if there are no more filters following. Code executed BEFORE calling this function is executed BEFORE the view or action, while code executed AFTER calling this function is executed AFTER the view of action. 112.8 + 112.9 +--]]-- 112.10 + 112.11 +function execute.inner() 112.12 + local stack = execute._wrap_stack 112.13 + local pos = #stack 112.14 + if pos == 0 then 112.15 + error("Unexpected call of execute.inner().") 112.16 + end 112.17 + local inner_func = stack[pos] 112.18 + if not inner_func then 112.19 + error("Repeated call of execute.inner().") 112.20 + end 112.21 + stack[pos] = false 112.22 + inner_func() 112.23 +end
113.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 113.2 +++ b/framework/env/execute/multi_wrapped.lua Sun Oct 25 12:00:00 2009 +0100 113.3 @@ -0,0 +1,26 @@ 113.4 +--[[-- 113.5 +execute.multi_wrapped( 113.6 + wrapper_funcs, -- multiple wrapper functions (i.e. filters) 113.7 + inner_func -- inner function (i.e. an action or view) 113.8 +) 113.9 + 113.10 +This function does the same as execute.wrapped(...), but with multiple wrapper functions, instead of just one wrapper function. It is used by execute.filtered_view{...} and execute.filtered_action{...} to wrap multiple filters around the view or action. 113.11 + 113.12 +--]]-- 113.13 + 113.14 +function execute.multi_wrapped(wrapper_funcs, inner_func) 113.15 + local function wrapped_execution(pos) 113.16 + local wrapper_func = wrapper_funcs[pos] 113.17 + if wrapper_func then 113.18 + return execute.wrapped( 113.19 + wrapper_func, 113.20 + function() 113.21 + wrapped_execution(pos+1) 113.22 + end 113.23 + ) 113.24 + else 113.25 + return inner_func() 113.26 + end 113.27 + end 113.28 + return wrapped_execution(1) 113.29 +end
114.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 114.2 +++ b/framework/env/execute/view.lua Sun Oct 25 12:00:00 2009 +0100 114.3 @@ -0,0 +1,26 @@ 114.4 +--[[-- 114.5 +execute.view{ 114.6 + module = module, -- module name of the view to be executed 114.7 + view = view, -- name of the view to be executed 114.8 + id = id, -- id to be returned by param.get_id(...) during execution 114.9 + params = params -- parameters to be returned by param.get(...) during execution 114.10 +} 114.11 + 114.12 +Executes a view directly (without associated filters). 114.13 + 114.14 +--]]-- 114.15 + 114.16 +function execute.view(args) 114.17 + local module = args.module 114.18 + local view = args.view 114.19 + trace.enter_view{ module = module, view = view } 114.20 + execute.file_path{ 114.21 + file_path = encode.file_path( 114.22 + request.get_app_basepath(), 114.23 + 'app', request.get_app_name(), module, view .. '.lua' 114.24 + ), 114.25 + id = args.id, 114.26 + params = args.params 114.27 + } 114.28 + trace.execution_return() 114.29 +end
115.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 115.2 +++ b/framework/env/execute/wrapped.lua Sun Oct 25 12:00:00 2009 +0100 115.3 @@ -0,0 +1,26 @@ 115.4 +--[[-- 115.5 +execute.wrapped( 115.6 + wrapper_func, -- function with an execute.inner() call inside 115.7 + inner_func -- function which is executed when execute.inner() is called 115.8 +) 115.9 + 115.10 +This function takes two functions as argument. The first function is executed, and must contain one call of execute.inner() during its execution. When execute.inner() is called, the second function is executed. After the second function finished, program flow continues in the first function. 115.11 + 115.12 +--]]-- 115.13 + 115.14 +function execute.wrapped(wrapper_func, inner_func) 115.15 + if 115.16 + type(wrapper_func) ~= "function" or 115.17 + type(inner_func) ~= "function" 115.18 + then 115.19 + error("Two functions need to be passed to execute.wrapped(...).") 115.20 + end 115.21 + local stack = execute._wrap_stack 115.22 + local pos = #stack + 1 115.23 + stack[pos] = inner_func 115.24 + wrapper_func() 115.25 + -- if stack[pos] then 115.26 + -- error("Wrapper function did not call execute.inner().") 115.27 + -- end 115.28 + stack[pos] = nil 115.29 +end
116.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 116.2 +++ b/framework/env/format/boolean.lua Sun Oct 25 12:00:00 2009 +0100 116.3 @@ -0,0 +1,30 @@ 116.4 +--[[-- 116.5 +text = -- human text representation of the boolean 116.6 +format.boolean( 116.7 + value, -- true, false or nil 116.8 + { 116.9 + true_as = true_text, -- text representing true 116.10 + false_as = false_text, -- text representing false 116.11 + nil_as = nil_text -- text representing nil 116.12 + } 116.13 +) 116.14 + 116.15 +Returns a human readable text representation of a boolean value. Additional parameters should be given, unless you like the defaults for false and true, which are "0" and "1". 116.16 + 116.17 +--]]-- 116.18 + 116.19 +function format.boolean(value, options) 116.20 + local options = options or {} 116.21 + local true_text = options.true_as or "Yes" -- TODO: localization? 116.22 + local false_text = options.false_as or "No" -- TODO: localization? 116.23 + local nil_text = options.nil_as or "" 116.24 + if value == nil then 116.25 + return nil_text 116.26 + elseif value == false then 116.27 + return false_text 116.28 + elseif value == true then 116.29 + return true_text 116.30 + else 116.31 + error("Value passed to format.boolean(...) is neither a boolean nor nil.") 116.32 + end 116.33 +end
117.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 117.2 +++ b/framework/env/format/currency.lua Sun Oct 25 12:00:00 2009 +0100 117.3 @@ -0,0 +1,50 @@ 117.4 +--[[-- 117.5 +text = 117.6 +format.currency( 117.7 + value, 117.8 + { 117.9 + nil_as = nil_text, -- text to be returned for a nil value 117.10 + digits = digits, -- number of digits before the decimal point 117.11 + currency_precision = currency_precision, -- number of digits after decimal point 117.12 + currency_prefix = currency_prefix, -- prefix string, i.e. "$ " 117.13 + currency_decimal_point = currency_decimal_point, -- string to be used as decimal point 117.14 + currency_suffix = currency_suffix, -- suffix string, i.e. " EUR" 117.15 + hide_unit = hide_unit, -- hide the currency unit, if true 117.16 + decimal_point = decimal_point -- used instead of 'currency_decimal_point', if 'hide_unit' is true 117.17 + } 117.18 +) 117.19 + 117.20 +Formats a (floating point) number or a fraction as a decimal number. If a 'digits' option is set, the number of digits before the decimal point is increased up to the given count by preceding it with zeros. The digits after the decimal point are adjusted by the 'precision' parameter. The 'decimal_shift' parameter is useful, when fixed precision decimal numbers are stored as integers, as the given value will be divided by 10 to the power of the 'decimal_shift' value prior to formatting. Setting 'decimal_shift' to true will use the 'precision' value as 'decimal_shift'. 117.21 + 117.22 +--]]-- 117.23 + 117.24 +function format.currency(value, options) 117.25 + local options = table.new(options) 117.26 + local prefix 117.27 + local suffix 117.28 + if options.hide_unit then 117.29 + prefix = "" 117.30 + suffix = "" 117.31 + options.decimal_point = 117.32 + options.decimal_point or locale.get("decimal_point") 117.33 + options.precision = 117.34 + options.currency_precision or locale.get("currency_precision") or 2 117.35 + elseif 117.36 + options.currency_prefix or options.currency_suffix or 117.37 + options.currency_precision or options.currency_decimal_point 117.38 + then 117.39 + prefix = options.currency_prefix or '' 117.40 + suffix = options.currency_suffix or '' 117.41 + options.decimal_point = options.currency_decimal_point 117.42 + options.precision = options.currency_precision or 2 117.43 + else 117.44 + prefix = locale.get("currency_prefix") or '' 117.45 + suffix = locale.get("currency_suffix") or '' 117.46 + options.decimal_point = locale.get("currency_decimal_point") 117.47 + options.precision = locale.get("currency_precision") or 2 117.48 + end 117.49 + if value == nil then 117.50 + return options.nil_as or '' 117.51 + end 117.52 + return prefix .. format.decimal(value, options) .. suffix 117.53 +end
118.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 118.2 +++ b/framework/env/format/date.lua Sun Oct 25 12:00:00 2009 +0100 118.3 @@ -0,0 +1,51 @@ 118.4 +--[[-- 118.5 +text = -- text with the value formatted as a date, according to the locale settings 118.6 +format.date( 118.7 + value, -- a date, a timestamp or nil 118.8 + { 118.9 + nil_as = nil_text -- text to be returned for a nil value 118.10 + } 118.11 +) 118.12 + 118.13 +Formats a date or timestamp as a date, according to the locale settings. 118.14 + 118.15 +--]]-- 118.16 + 118.17 +function format.date(value, options) 118.18 + local options = options or {} 118.19 + if value == nil then 118.20 + return options.nil_as or "" 118.21 + end 118.22 + if not ( 118.23 + atom.has_type(value, atom.date) or 118.24 + atom.has_type(value, atom.timestamp) 118.25 + ) then 118.26 + error("Value passed to format.date(...) is neither a date, a timestamp, nor nil.") 118.27 + end 118.28 + if value.invalid then 118.29 + return "invalid" 118.30 + end 118.31 + local result = locale.get("date_format") or "YYYY-MM-DD" 118.32 + result = string.gsub(result, "YYYY", function() 118.33 + return format.decimal(value.year, { digits = 4 }) 118.34 + end) 118.35 + result = string.gsub(result, "YY", function() 118.36 + return format.decimal(value.year % 100, { digits = 2 }) 118.37 + end) 118.38 + result = string.gsub(result, "Y", function() 118.39 + return format.decimal(value.year) 118.40 + end) 118.41 + result = string.gsub(result, "MM", function() 118.42 + return format.decimal(value.month, { digits = 2 }) 118.43 + end) 118.44 + result = string.gsub(result, "M", function() 118.45 + return format.decimal(value.month) 118.46 + end) 118.47 + result = string.gsub(result, "DD", function() 118.48 + return format.decimal(value.day, { digits = 2 }) 118.49 + end) 118.50 + result = string.gsub(result, "D", function() 118.51 + return format.decimal(value.day) 118.52 + end) 118.53 + return result 118.54 +end
119.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 119.2 +++ b/framework/env/format/decimal.lua Sun Oct 25 12:00:00 2009 +0100 119.3 @@ -0,0 +1,89 @@ 119.4 +--[[-- 119.5 +text = -- text with the value formatted as decimal number 119.6 +format.decimal( 119.7 + value, -- a number, a fraction or nil 119.8 + { 119.9 + nil_as = nil_text, -- text to be returned for a nil value 119.10 + digits = digits, -- digits before decimal point 119.11 + precision = precision, -- digits after decimal point 119.12 + decimal_shift = decimal_shift -- divide the value by 10^decimal_shift (setting true uses precision) 119.13 + } 119.14 +) 119.15 + 119.16 +Formats a (floating point) number or a fraction as a decimal number. If a 'digits' option is set, the number of digits before the decimal point is increased up to the given count by preceding it with zeros. The digits after the decimal point are adjusted by the 'precision' parameter. The 'decimal_shift' parameter is useful, when fixed precision decimal numbers are stored as integers, as the given value will be divided by 10 to the power of the 'decimal_shift' value prior to formatting. Setting 'decimal_shift' to true will copy the value for 'precision'. 119.17 + 119.18 +--]]-- 119.19 + 119.20 +function format.decimal(value, options) 119.21 + -- TODO: more features 119.22 + local options = options or {} 119.23 + local special_chars = charset.get_data().special_chars 119.24 + local f 119.25 + if value == nil then 119.26 + return options.nil_as or "" 119.27 + elseif atom.has_type(value, atom.number) then 119.28 + f = value 119.29 + elseif atom.has_type(value, atom.fraction) then 119.30 + f = value.float 119.31 + else 119.32 + error("Value passed to format.decimal(...) is neither a number nor a fraction nor nil.") 119.33 + end 119.34 + local digits = options.digits 119.35 + local precision = options.precision or 0 119.36 + local decimal_shift = options.decimal_shift or 0 119.37 + if decimal_shift == true then 119.38 + decimal_shift = precision 119.39 + end 119.40 + f = f / 10 ^ decimal_shift 119.41 + local negative 119.42 + local absolute 119.43 + if f < 0 then 119.44 + absolute = -f 119.45 + negative = true 119.46 + else 119.47 + absolute = f 119.48 + negative = false 119.49 + end 119.50 + absolute = absolute + 0.5 / 10 ^ precision 119.51 + local int = math.floor(absolute) 119.52 + if not atom.is_integer(int) then 119.53 + if f > 0 then 119.54 + return "+" .. special_chars.inf_sign 119.55 + elseif f < 0 then 119.56 + return minus_sign .. special_chars.inf_sign 119.57 + else 119.58 + return "NaN" 119.59 + end 119.60 + end 119.61 + local int_str = tostring(int) 119.62 + if digits then 119.63 + while #int_str < digits do 119.64 + int_str = "0" .. int_str 119.65 + end 119.66 + end 119.67 + if precision > 0 then 119.68 + local decimal_point = 119.69 + options.decimal_point or 119.70 + locale.get('decimal_point') or '.' 119.71 + local frac_str = tostring(math.floor((absolute - int) * 10 ^ precision)) 119.72 + while #frac_str < precision do 119.73 + frac_str = "0" .. frac_str 119.74 + end 119.75 + assert(#frac_str == precision, "Assertion failed in format.float(...).") -- should not happen 119.76 + if negative then 119.77 + return special_chars.minus_sign .. int_str .. decimal_point .. frac_str 119.78 + elseif options.show_plus then 119.79 + return "+" .. int_str .. decimal_point .. frac_str 119.80 + else 119.81 + return int_str .. decimal_point .. frac_str 119.82 + end 119.83 + else 119.84 + if negative then 119.85 + return special_chars.minus_sign .. int 119.86 + elseif options.show_plus then 119.87 + return "+" .. int_str 119.88 + else 119.89 + return int_str 119.90 + end 119.91 + end 119.92 +end
120.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 120.2 +++ b/framework/env/format/percentage.lua Sun Oct 25 12:00:00 2009 +0100 120.3 @@ -0,0 +1,35 @@ 120.4 +--[[-- 120.5 +text = -- text with the value formatted as a percentage 120.6 +format.percentage( 120.7 + value, -- a number, a fraction or nil 120.8 + { 120.9 + nil_as = nil_text -- text to be returned for a nil value 120.10 + digits = digits, -- digits before decimal point (of the percentage value) 120.11 + precision = precision, -- digits after decimal point (of the percentage value) 120.12 + decimal_shift = decimal_shift -- divide the value by 10^decimal_shift (setting true uses precision + 2) 120.13 + } 120.14 +) 120.15 + 120.16 +Formats a number or fraction as a percentage. 120.17 + 120.18 +--]]-- 120.19 + 120.20 +function format.percentage(value, options) 120.21 + local options = table.new(options) 120.22 + local f 120.23 + if value == nil then 120.24 + return options.nil_as or "" 120.25 + elseif atom.has_type(value, atom.number) then 120.26 + f = value 120.27 + elseif atom.has_type(value, atom.fraction) then 120.28 + f = value.float 120.29 + else 120.30 + error("Value passed to format.percentage(...) is neither a number nor a fraction nor nil.") 120.31 + end 120.32 + options.precision = options.precision or 0 120.33 + if options.decimal_shift == true then 120.34 + options.decimal_shift = options.precision + 2 120.35 + end 120.36 + local suffix = options.hide_unit and "" or " %" 120.37 + return format.decimal(f * 100, options) .. suffix 120.38 +end
121.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 121.2 +++ b/framework/env/format/string.lua Sun Oct 25 12:00:00 2009 +0100 121.3 @@ -0,0 +1,21 @@ 121.4 +--[[-- 121.5 +text = -- a string 121.6 +format.string( 121.7 + value, -- any value where tostring(value) gives a reasonable result 121.8 + { 121.9 + nil_as = nil_text -- text to be returned for a nil value 121.10 + } 121.11 +) 121.12 + 121.13 +Formats a value as a text by calling tostring(...), unless the value is nil, in which case the text returned is chosen by the 'nil_as' option. 121.14 + 121.15 +--]]-- 121.16 + 121.17 +function format.string(str, options) 121.18 + local options = options or {} 121.19 + if str == nil then 121.20 + return options.nil_as or "" 121.21 + else 121.22 + return tostring(str) 121.23 + end 121.24 +end
122.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 122.2 +++ b/framework/env/format/time.lua Sun Oct 25 12:00:00 2009 +0100 122.3 @@ -0,0 +1,60 @@ 122.4 +--[[-- 122.5 +text = -- text with the value formatted as a time, according to the locale settings 122.6 +format.time( 122.7 + value, -- a time, a timestamp or nil 122.8 + { 122.9 + nil_as = nil_text -- text to be returned for a nil value 122.10 + } 122.11 +) 122.12 + 122.13 +Formats a time or timestamp as a time, according to the locale settings. 122.14 + 122.15 +--]]-- 122.16 + 122.17 +function format.time(value, options) 122.18 + local options = options or {} 122.19 + if value == nil then 122.20 + return options.nil_as or "" 122.21 + end 122.22 + if not ( 122.23 + atom.has_type(value, atom.time) or 122.24 + atom.has_type(value, atom.timestamp) 122.25 + ) then 122.26 + error("Value passed to format.time(...) is neither a time, a timestamp, nor nil.") 122.27 + end 122.28 + if value.invalid then 122.29 + return "invalid" 122.30 + end 122.31 + local result = locale.get("time_format") or "HH:MM{:SS}" 122.32 + if options.hide_seconds then 122.33 + result = string.gsub(result, "{[^{|}]*}", "") 122.34 + else 122.35 + result = string.gsub(result, "{([^|]*)}", "%1") 122.36 + end 122.37 + local am_pm 122.38 + local hour = value.hour 122.39 + result = string.gsub(result, "{([^{}]*)|([^{}]*)}", function(am, pm) 122.40 + if hour > 12 then 122.41 + am_pm = pm 122.42 + else 122.43 + am_pm = am 122.44 + end 122.45 + return "{|}" 122.46 + end) 122.47 + if am_pm and hour > 12 then 122.48 + hour = hour - 12 122.49 + end 122.50 + result = string.gsub(result, "HH", function() 122.51 + return format.decimal(hour, { digits = 2 }) 122.52 + end) 122.53 + result = string.gsub(result, "MM", function() 122.54 + return format.decimal(value.minute, { digits = 2 }) 122.55 + end) 122.56 + result = string.gsub(result, "SS", function() 122.57 + return format.decimal(value.second, { digits = 2 }) 122.58 + end) 122.59 + if am_pm then 122.60 + result = string.gsub(result, "{|}", am_pm) 122.61 + end 122.62 + return result 122.63 +end
123.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 123.2 +++ b/framework/env/format/timestamp.lua Sun Oct 25 12:00:00 2009 +0100 123.3 @@ -0,0 +1,19 @@ 123.4 +--[[-- 123.5 +text = -- text with the given timestamp value formatted according to the locale settings 123.6 +format.timestamp( 123.7 + value, -- a timestamp or nil 123.8 + { 123.9 + nil_as = nil_text -- text to be returned for a nil value 123.10 + } 123.11 +) 123.12 + 123.13 +Formats a timestamp according to the locale settings. 123.14 + 123.15 +--]]-- 123.16 + 123.17 +function format.timestamp(value, options) 123.18 + if value == nil then 123.19 + return options.nil_as or "" 123.20 + end 123.21 + return format.date(value, options) .. " " .. format.time(value, options) 123.22 +end
124.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 124.2 +++ b/framework/env/locale/__init.lua Sun Oct 25 12:00:00 2009 +0100 124.3 @@ -0,0 +1,4 @@ 124.4 +locale._current_data = {} 124.5 +locale._translation_tables = {} 124.6 +locale._empty_translation_table = {} 124.7 +
125.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 125.2 +++ b/framework/env/locale/_get_translation_table.lua Sun Oct 25 12:00:00 2009 +0100 125.3 @@ -0,0 +1,23 @@ 125.4 +function locale._get_translation_table() 125.5 + local language_code = locale.get("lang") 125.6 + if language_code then 125.7 + if type(language_code) ~= "string" then 125.8 + error('locale.get("lang") does not return a string.') 125.9 + end 125.10 + local translation_table = locale._translation_tables[language_code] 125.11 + if translation_table then 125.12 + return translation_table 125.13 + end 125.14 + local filename = encode.file_path(request.get_app_basepath(), "locale", "translations." .. language_code .. ".lua") 125.15 + local func = assert(loadfile(filename)) 125.16 + setfenv(func, {}) 125.17 + translation_table = func() 125.18 + if type(translation_table) ~= "table" then 125.19 + error("Translation file did not return a table.") 125.20 + end 125.21 + locale._translation_tables[language_code] = translation_table 125.22 + return translation_table 125.23 + else 125.24 + return locale._empty_translation_table 125.25 + end 125.26 +end
126.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 126.2 +++ b/framework/env/locale/do_with.lua Sun Oct 25 12:00:00 2009 +0100 126.3 @@ -0,0 +1,22 @@ 126.4 +--[[-- 126.5 +locale.do_with( 126.6 + locale_options, -- table with locale information (as if passed to locale.set(...)) 126.7 + function() 126.8 + ... -- code to be executed with the given locale settings 126.9 + end 126.10 +) 126.11 + 126.12 +This function executes code with temporarily changed locale settings. See locale.set(...) for correct usage of 'locale_options'. 126.13 + 126.14 +--]]-- 126.15 + 126.16 +function locale.do_with(locale_options, block) 126.17 + local old_data = {} 126.18 + for key, value in pairs(locale._current_data) do 126.19 + old_data[key] = value 126.20 + end 126.21 + locale.set(locale_options) 126.22 + block() 126.23 + old_data.reset = true 126.24 + locale.set(old_data) 126.25 +end
127.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 127.2 +++ b/framework/env/locale/get.lua Sun Oct 25 12:00:00 2009 +0100 127.3 @@ -0,0 +1,13 @@ 127.4 +--[[-- 127.5 +locale_setting = -- setting for the given localization category (could be of any type, depending on the category) 127.6 +locale.get( 127.7 + category -- string selecting a localization category, e.g. "lang" or "time", etc... 127.8 +) 127.9 + 127.10 +This function is used to read locale settings, which have been set with locale.set(...) or locale.do_with(...). 127.11 + 127.12 +--]]-- 127.13 + 127.14 +function locale.get(category) 127.15 + return locale._current_data[category] 127.16 +end
128.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 128.2 +++ b/framework/env/locale/set.lua Sun Oct 25 12:00:00 2009 +0100 128.3 @@ -0,0 +1,19 @@ 128.4 +--[[-- 128.5 +locale.set( 128.6 + locale_options -- table with locale categories as keys and their settings as values 128.7 +) 128.8 + 128.9 +This function is used to set locale settings. The table given as first and only argument contains locale categories (e.g. "lang" or "time") as keys, and their settings as values. If there is a key 'reset' with a true value, then all non mentioned categories will be reset to nil. 128.10 + 128.11 +--]]-- 128.12 + 128.13 +function locale.set(locale_options) 128.14 + if locale_options.reset then 128.15 + locale._current_data = {} 128.16 + end 128.17 + for key, value in pairs(locale_options) do 128.18 + if key ~= "reset" then 128.19 + locale._current_data[key] = value 128.20 + end 128.21 + end 128.22 +end
129.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 129.2 +++ b/framework/env/net/send_mail.lua Sun Oct 25 12:00:00 2009 +0100 129.3 @@ -0,0 +1,57 @@ 129.4 +--[[-- 129.5 +net.send_mail{ 129.6 + envelope_from = envelope_from, -- envelope from address, not part of mail headers 129.7 + from = from, -- From header address or table with 'name' and 'address' fields 129.8 + sender = sender, -- Sender header address or table with 'name' and 'address' fields 129.9 + reply_to = reply_to, -- Reply-To header address or table with 'name' and 'address' fields 129.10 + to = to, -- To header address or table with 'name' and 'address' fields 129.11 + cc = cc, -- Cc header address or table with 'name' and 'address' fields 129.12 + bcc = bcc, -- Bcc header address or table with 'name' and 'address' fields 129.13 + subject = subject, -- subject of e-mail 129.14 + multipart = multipart_type, -- "alternative", "mixed", "related", or nil 129.15 + content_type = content_type, -- only for multipart == nil, defaults to "text/plain" 129.16 + binary = binary, -- allow full 8-bit content 129.17 + content = content or { -- content as lua-string, or table in case of multipart 129.18 + { 129.19 + multipart = multipart_type, 129.20 + ..., 129.21 + content = content or { 129.22 + {...}, ... 129.23 + } 129.24 + }, { 129.25 + ... 129.26 + }, 129.27 + ... 129.28 + } 129.29 +} 129.30 + 129.31 +This function sends a mail using the /usr/sbin/sendmail command. 129.32 + 129.33 +--]]-- 129.34 + 129.35 +function net.send_mail(args) 129.36 + local mail 129.37 + if type(args) == "string" then 129.38 + mail = args 129.39 + else 129.40 + mail = encode.mime.mail(args) 129.41 + end 129.42 + local envelope_from = args.envelope_from 129.43 + local command 129.44 + if 129.45 + envelope_from and 129.46 + string.find(envelope_from, "^[0-9A-Za-z%.-_@0-9A-Za-z%.-_]+$") 129.47 + then 129.48 + command = 129.49 + "/usr/sbin/sendmail -t -i -f " .. 129.50 + envelope_from .. 129.51 + " > /dev/null 2> /dev/null" 129.52 + else 129.53 + command = "/usr/sbin/sendmail -t -i > /dev/null 2> /dev/null" 129.54 + end 129.55 + trace.debug(command) 129.56 + -- TODO: use pfilter 129.57 + local sendmail = assert(io.popen(command, "w")) 129.58 + sendmail:write(mail) 129.59 + sendmail:close() 129.60 +end
130.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 130.2 +++ b/framework/env/param/__init.lua Sun Oct 25 12:00:00 2009 +0100 130.3 @@ -0,0 +1,2 @@ 130.4 +param._exchanged = false -- important to be false, not nil 130.5 +param._saved = {} -- stack
131.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 131.2 +++ b/framework/env/param/_get_parser.lua Sun Oct 25 12:00:00 2009 +0100 131.3 @@ -0,0 +1,72 @@ 131.4 +function param._get_parser(format_info, param_type) 131.5 + if format_info == nil or format_info == "" then 131.6 + return function(str) 131.7 + return param_type:load(str) 131.8 + end 131.9 + else 131.10 + local format_options = {} 131.11 + local format_type, rest = string.match( 131.12 + format_info, 131.13 + "^([A-Za-z][A-Za-z0-9_]*)(.*)$" 131.14 + ) 131.15 + if format_type then 131.16 + rest = string.gsub(rest, "\\\\", "\\e") 131.17 + rest = string.gsub(rest, "\\'", "\\q") 131.18 + if 131.19 + string.find(rest, "\\$") or 131.20 + string.find(rest, "\\[^eq]") 131.21 + then 131.22 + format_type = nil 131.23 + else 131.24 + while rest ~= "" do 131.25 + local key, value, new_rest 131.26 + key, value, new_rest = string.match( 131.27 + rest, 131.28 + "^-([A-Za-z][A-Za-z0-9_]*)-'([^']*)'(.*)$" 131.29 + ) 131.30 + if value then 131.31 + value = string.gsub(value, "\\q", "'") 131.32 + value = string.gsub(value, "\\e", "\\") 131.33 + format_options[key] = value 131.34 + else 131.35 + key, value, new_rest = string.match( 131.36 + rest, 131.37 + "^-([A-Za-z][A-Za-z0-9_]*)-([^-]*)(.*)$" 131.38 + ) 131.39 + if value then 131.40 + if string.find(value, "^[0-9.Ee+-]+$") then 131.41 + local num = tonumber(value) 131.42 + if not num then 131.43 + format_type = nil 131.44 + break 131.45 + end 131.46 + format_options[key] = num 131.47 + elseif value == "t" then 131.48 + format_options[key] = true 131.49 + elseif value == "f" then 131.50 + format_options[key] = false 131.51 + else 131.52 + format_type = nil 131.53 + break 131.54 + end 131.55 + else 131.56 + format_type = nil 131.57 + break 131.58 + end 131.59 + end 131.60 + rest = new_rest 131.61 + end 131.62 + end 131.63 + end 131.64 + if not format_type then 131.65 + error("Illegal format string in GET/POST parameters found.") 131.66 + end 131.67 + local parse_func = parse[format_type] 131.68 + if not parse_func then 131.69 + error("Unknown format identifier in GET/POST parameters encountered.") 131.70 + end 131.71 + return function(str) 131.72 + return parse_func(str, param_type, format_options) 131.73 + end 131.74 + end 131.75 +end
132.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 132.2 +++ b/framework/env/param/exchange.lua Sun Oct 25 12:00:00 2009 +0100 132.3 @@ -0,0 +1,14 @@ 132.4 +--[[-- 132.5 +param.exchange( 132.6 + id, 132.7 + params 132.8 +) 132.9 + 132.10 +This function exchanges the id and/or parameters which are returned by param.get_id(...), param.get(...), etc. It should not be called explicitly, but is used implicitly, if you pass an 'id' or 'params' option to execute.view{...} or execute.action{...}. The function param.restore() is used to revert the exchange. 132.11 + 132.12 +--]]-- 132.13 + 132.14 +function param.exchange(id, params) 132.15 + table.insert(param._saved, param._exchanged) 132.16 + param._exchanged = { id = id, params = params } 132.17 +end
133.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 133.2 +++ b/framework/env/param/get.lua Sun Oct 25 12:00:00 2009 +0100 133.3 @@ -0,0 +1,31 @@ 133.4 +--[[-- 133.5 +value = -- value of the parameter casted to the chosen param_type 133.6 +param.get( 133.7 + key, -- name of the parameter 133.8 + param_type -- desired type of the returned value 133.9 +) 133.10 + 133.11 +Either a GET or POST request parameter is returned by this function, or if param.exchange(...) was called before, one of the exchanged parameters is returned. You can specify which type the returned value shall have. If an external request parameter was used and there is another GET or POST parameter with the same name but a "__format" suffix, the parser with the name of the specified format will be automatically used to parse and convert the input value. 133.12 + 133.13 +--]]-- 133.14 + 133.15 +function param.get(key, param_type) 133.16 + local param_type = param_type or atom.string 133.17 + if param._exchanged then 133.18 + local value = param._exchanged.params[key] 133.19 + if value ~= nil and not atom.has_type(value, param_type) then 133.20 + error("Parameter has unexpected type.") 133.21 + end 133.22 + return value 133.23 + else 133.24 + local str = cgi.params[key] 133.25 + local format_info = cgi.params[key .. "__format"] 133.26 + if not str then 133.27 + if not format_info then 133.28 + return nil 133.29 + end 133.30 + str = "" 133.31 + end 133.32 + return param._get_parser(format_info, param_type)(str) 133.33 + end 133.34 +end
134.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 134.2 +++ b/framework/env/param/get_all_cgi.lua Sun Oct 25 12:00:00 2009 +0100 134.3 @@ -0,0 +1,20 @@ 134.4 +--[[-- 134.5 +params = -- table with all non-list parameters 134.6 +param.get_all_cgi() 134.7 + 134.8 +This function returns a table with all non-list GET/POST parameters (except internal parameters like "_webmcp_id"). 134.9 + 134.10 +--]]-- 134.11 + 134.12 +function param.get_all_cgi() 134.13 + local result = {} 134.14 + for key, value in pairs(cgi.params) do -- TODO: exchanged params too? 134.15 + if 134.16 + (not string.match(key, "^_webmcp_")) and 134.17 + (not string.match(key, "%[%]$")) 134.18 + then 134.19 + result[key] = value 134.20 + end 134.21 + end 134.22 + return result 134.23 +end
135.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 135.2 +++ b/framework/env/param/get_id.lua Sun Oct 25 12:00:00 2009 +0100 135.3 @@ -0,0 +1,22 @@ 135.4 +--[[-- 135.5 +value = -- value of the id casted to the chosen param_type 135.6 +param.get_id( 135.7 + param_type -- desired type of the returned value 135.8 +) 135.9 + 135.10 +Same as param.get(...), but operates on a special id parameter. An id is set via a __webmcp_id GET or POST parameter or an 'id' option to execute.view{...} or execute.action{...}. In a normal setup a beauty URL of the form http://www.example.com/example-application/example-module/example-view/<id>.html will cause the id to be set. 135.11 + 135.12 +--]]-- 135.13 + 135.14 +function param.get_id(param_type) 135.15 + local param_type = param_type or atom.integer 135.16 + if param._exchanged then 135.17 + local value = param._exchanged.id 135.18 + if value ~= nil and not atom.has_type(value, param_type) then 135.19 + error("Parameter has unexpected type.") 135.20 + end 135.21 + return value 135.22 + else 135.23 + return param.get("_webmcp_id", param_type) 135.24 + end 135.25 +end
136.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 136.2 +++ b/framework/env/param/get_id_cgi.lua Sun Oct 25 12:00:00 2009 +0100 136.3 @@ -0,0 +1,11 @@ 136.4 +--[[-- 136.5 +value = -- id string or nil 136.6 +param.get_id_cgi() 136.7 + 136.8 +This function returns the string value of the _webmcp_id GET/POST parameter. 136.9 + 136.10 +--]]-- 136.11 + 136.12 +function param.get_id_cgi() 136.13 + return cgi.params._webmcp_id 136.14 +end
137.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 137.2 +++ b/framework/env/param/get_list.lua Sun Oct 25 12:00:00 2009 +0100 137.3 @@ -0,0 +1,37 @@ 137.4 +--[[-- 137.5 +values = -- list of values casted to the chosen param_type 137.6 +param.get_list( 137.7 + key, -- name of the parameter without "[]" suffix 137.8 + param_type, -- desired type of the returned values 137.9 +) 137.10 + 137.11 +Same as param.get(...), but used for parameters which contain a list of values. For external GET/POST parameters the parameter name gets suffixed with "[]". 137.12 + 137.13 +--]]-- 137.14 + 137.15 +function param.get_list(key, param_type) 137.16 + local param_type = param_type or atom.string 137.17 + if param._exchanged then 137.18 + local values = param._exchanged.params[key] or {} 137.19 + if type(values) ~= "table" then 137.20 + error("Parameter has unexpected type.") 137.21 + end 137.22 + for idx, value in ipairs(values) do 137.23 + if not atom.has_type(value, param_type) then 137.24 + error("Element of parameter list has unexpected type.") 137.25 + end 137.26 + end 137.27 + return values 137.28 + else 137.29 + local format_info = cgi.params[key .. "__format"] 137.30 + local parser = param._get_parser(format_info, param_type) 137.31 + local raw_values = cgi.params[key .. "[]"] 137.32 + local values = {} 137.33 + if raw_values then 137.34 + for idx, value in ipairs(raw_values) do 137.35 + values[idx] = parser(raw_values[idx]) 137.36 + end 137.37 + end 137.38 + return values 137.39 + end 137.40 +end
138.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 138.2 +++ b/framework/env/param/iterate.lua Sun Oct 25 12:00:00 2009 +0100 138.3 @@ -0,0 +1,29 @@ 138.4 +--[[-- 138.5 +for 138.6 + index, -- index variable counting up from 1 138.7 + prefix -- prefix string with index in square brackets to be used as a prefix for a key passed to param.get or param.get_list 138.8 +in 138.9 + param.iterate( 138.10 + prefix -- prefix to be followed by an index in square brackets and another key 138.11 + ) 138.12 +do 138.13 + ... 138.14 +end 138.15 + 138.16 +This function returns an interator function to be used in a for loop. The CGI GET/POST parameter (or internal parameter) with the name "prefix[len]" is read, where 'prefix' is the prefix passed as the argument and 'len' ist just the literal string "len". For each index from 1 to the read length the returned iterator function returns the index and a string consisting of the given prefix followed by the index in square brackets to be used as a prefix for keys passed to param.get(...) or param.get_list(...). 138.17 + 138.18 +--]]-- 138.19 + 138.20 +function param.iterate(prefix) 138.21 + local length = param.get(prefix .. "[len]", atom.integer) or 0 138.22 + if not atom.is_integer(length) then 138.23 + error("List length is not a valid integer or nil.") 138.24 + end 138.25 + local index = 0 138.26 + return function() 138.27 + index = index + 1 138.28 + if index <= length then 138.29 + return index, prefix .. "[" .. index .. "]" 138.30 + end 138.31 + end 138.32 +end
139.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 139.2 +++ b/framework/env/param/restore.lua Sun Oct 25 12:00:00 2009 +0100 139.3 @@ -0,0 +1,16 @@ 139.4 +--[[-- 139.5 +param.restore() 139.6 + 139.7 +Calling this function reverts the changes of param.exchange(...). It should not be called explicitly, but is used implicitly, if you pass an 'id' or 'params' option to execute.view{...} or execute.action{...}. 139.8 + 139.9 +--]]-- 139.10 + 139.11 +function param.restore() 139.12 + local saved = param._saved 139.13 + local previous = saved[#saved] 139.14 + saved[#saved] = nil 139.15 + if previous == nil then 139.16 + error("Tried to restore id and params without having exchanged it before.") 139.17 + end 139.18 + param._exchanged = previous 139.19 +end
140.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 140.2 +++ b/framework/env/param/update.lua Sun Oct 25 12:00:00 2009 +0100 140.3 @@ -0,0 +1,40 @@ 140.4 +--[[-- 140.5 +param.update( 140.6 + record, -- database record to be updated 140.7 + key_and_field_name1, -- name of CGI parameter and record field 140.8 + key_and_field_name2, -- another name of a CGI parameter and record field 140.9 + { 140.10 + key3, -- name of CGI parameter 140.11 + field_name3 -- name of record field 140.12 + } 140.13 +) 140.14 + 140.15 +This function can update several fields of a database record using GET/POST request parameters (or internal/exchanged parameters). The type of each parameter is automatically determined by the class of the record (_class:get_colums()[field].type). 140.16 +--]]-- 140.17 + 140.18 +function param.update(record, mapping_info, ...) 140.19 + if not mapping_info then 140.20 + return 140.21 + end 140.22 + assert(record, "No record given for param.update(...).") 140.23 + assert(record._class, "Record passed to param.update(...) has no _class attribute.") 140.24 + local key, field_name 140.25 + if type(mapping_info) == "string" then 140.26 + key = mapping_info 140.27 + field_name = mapping_info 140.28 + else 140.29 + key = mapping_info[1] 140.30 + field_name = mapping_info[2] 140.31 + end 140.32 + assert(key, "No key given in parameter of param.update(...).") 140.33 + assert(field_name, "No field name given in parameter of param.update(...).") 140.34 + local column_info = record._class:get_columns()[field_name] 140.35 + if not column_info then 140.36 + error('Type of column "' .. field_name .. '" is unknown.') 140.37 + end 140.38 + local new_value = param.get(key, column_info.type) 140.39 + if new_value ~= record[field_name] then 140.40 + record[field_name] = new_value 140.41 + end 140.42 + return param.update(record, ...) -- recursivly process following arguments 140.43 +end
141.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 141.2 +++ b/framework/env/param/update_relationship.lua Sun Oct 25 12:00:00 2009 +0100 141.3 @@ -0,0 +1,57 @@ 141.4 +--[[-- 141.5 +param.update_relationship{ 141.6 + param_name = param_name, -- name of request GET/POST request parameters containing primary keys for model B 141.7 + id = id, -- value of the primary key for model A 141.8 + connecting_model = connecting_model, -- model used for creating/deleting entries referencing both model A and B 141.9 + own_reference = own_reference, -- field name for foreign key in the connecting model referencing model A 141.10 + foreign_reference = foreign_reference -- field name for foreign key in the connecting model referencing model B 141.11 +} 141.12 + 141.13 +This function updates a many-to-many relationship using a specified 'connecting_model' referencing both models. 141.14 + 141.15 +--]]-- 141.16 + 141.17 +function param.update_relationship(args) 141.18 + local param_name = args.param_name 141.19 + local id = args.id 141.20 + local connecting_model = args.connecting_model 141.21 + local own_reference = args.own_reference 141.22 + local foreign_reference = args.foreign_reference 141.23 + local selected_ids = param.get_list(param_name, atom.integer) -- TODO: support other types than integer too 141.24 + local db = connecting_model:get_db_conn() 141.25 + local table = connecting_model:get_qualified_table() 141.26 + if #selected_ids == 0 then 141.27 + db:query{ 141.28 + 'DELETE FROM ' .. table .. ' WHERE "' .. own_reference .. '" = ?', 141.29 + args.id 141.30 + } 141.31 + else 141.32 + local selected_ids_sql = { sep = ", " } 141.33 + for idx, value in ipairs(selected_ids) do 141.34 + selected_ids_sql[idx] = {"?::int8", value} 141.35 + end 141.36 + db:query{ 141.37 + 'DELETE FROM ' .. table .. 141.38 + ' WHERE "' .. own_reference .. '" = ?' .. 141.39 + ' AND NOT "' .. foreign_reference .. '" IN ($)', 141.40 + args.id, 141.41 + selected_ids_sql 141.42 + } 141.43 + -- TODO: use VALUES SQL command, instead of this dirty array trick 141.44 + db:query{ 141.45 + 'INSERT INTO ' .. table .. 141.46 + ' ("' .. own_reference .. '", "' .. foreign_reference .. '")' .. 141.47 + ' SELECT ?, "subquery"."foreign" FROM (' .. 141.48 + 'SELECT (ARRAY[$])[i] AS "foreign"' .. 141.49 + ' FROM generate_series(1, ?) AS "dummy"("i")' .. 141.50 + ' EXCEPT SELECT "' .. foreign_reference .. '" AS "foreign"' .. 141.51 + ' FROM ' .. table .. 141.52 + ' WHERE "' .. own_reference .. '" = ?' .. 141.53 + ') AS "subquery"', 141.54 + args.id, 141.55 + selected_ids_sql, 141.56 + #selected_ids, 141.57 + args.id 141.58 + } 141.59 + end 141.60 +end
142.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 142.2 +++ b/framework/env/parse/_pre_fold.lua Sun Oct 25 12:00:00 2009 +0100 142.3 @@ -0,0 +1,21 @@ 142.4 +function parse._pre_fold(str) 142.5 + local str = str 142.6 + local special_chars = charset.get_data().special_chars 142.7 + local function replace(name, dst) 142.8 + local src = special_chars[name] 142.9 + if src then 142.10 + local pattern = string.gsub(src, "[][()^$%%]", "%%%1") 142.11 + str = string.gsub(str, pattern, dst) 142.12 + end 142.13 + end 142.14 + replace("nobreak_space", " ") 142.15 + replace("minus_sign", "-") 142.16 + replace("hyphen_sign", "-") 142.17 + replace("nobreak_hyphen", "-") 142.18 + replace("figure_dash", "-") 142.19 + str = string.gsub(str, "\t+", " ") 142.20 + str = string.gsub(str, "^ +", "") 142.21 + str = string.gsub(str, " +$", "") 142.22 + str = string.gsub(str, " +", " ") 142.23 + return str 142.24 +end
143.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 143.2 +++ b/framework/env/parse/boolean.lua Sun Oct 25 12:00:00 2009 +0100 143.3 @@ -0,0 +1,29 @@ 143.4 +function parse.boolean(str, dest_type, options) 143.5 + if dest_type ~= atom.boolean then 143.6 + error("parse.boolean(...) can only return booleans, but a different destination type than atom.boolean was given.") 143.7 + end 143.8 + local options = options or {} 143.9 + local trimmed_str = string.match(str or "", "^%s*(.-)%s*$") 143.10 + if options.true_as or options.false_as or options.nil_as then 143.11 + if trimmed_str == options.true_as then 143.12 + return true 143.13 + elseif trimmed_str == options.false_as then 143.14 + return false 143.15 + elseif trimmed_str == options.nil_as or trimmed_str == "" then 143.16 + return nil 143.17 + else 143.18 + error("Boolean value not recognized.") 143.19 + end 143.20 + else 143.21 + local char = string.upper(string.sub(trimmed_str, 1, 1)) 143.22 + if char == "1" or char == "T" or char == "Y" then 143.23 + return true 143.24 + elseif char == "0" or char == "F" or char == "N" then 143.25 + return false 143.26 + elseif char == "" then 143.27 + return nil 143.28 + else 143.29 + error("Boolean value not recognized.") 143.30 + end 143.31 + end 143.32 +end
144.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 144.2 +++ b/framework/env/parse/currency.lua Sun Oct 25 12:00:00 2009 +0100 144.3 @@ -0,0 +1,29 @@ 144.4 +local function extract_numbers(str) 144.5 + local result = {} 144.6 + for char in string.gmatch(str, "[0-9]") do 144.7 + result[#result+1] = char 144.8 + end 144.9 + return table.concat(result) 144.10 +end 144.11 + 144.12 +function parse.currency(str, dest_type, options) 144.13 + local str = parse._pre_fold(str) 144.14 + local dest_type = dest_type or atom.number 144.15 + local options = options or {} 144.16 + local currency_decimal_point = locale.get("currency_decimal_point") 144.17 + local decimal_point = locale.get("decimal_point") or "." 144.18 + local pos1, pos2 144.19 + if currency_decimal_point then 144.20 + pos1, pos2 = string.find(str, currency_decimal_point, 1, true) 144.21 + end 144.22 + if not pos1 then 144.23 + pos1, pos2 = string.find(str, decimal_point, 1, true) 144.24 + end 144.25 + if pos1 then 144.26 + local p1 = extract_numbers(string.sub(str, 1, pos1 - 1)) 144.27 + local p2 = extract_numbers(string.sub(str, pos2 + 1, #str)) 144.28 + return parse.decimal(p1 .. decimal_point .. p2, dest_type, options) 144.29 + else 144.30 + return parse.decimal(extract_numbers(str), dest_type, options) 144.31 + end 144.32 +end
145.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 145.2 +++ b/framework/env/parse/date.lua Sun Oct 25 12:00:00 2009 +0100 145.3 @@ -0,0 +1,101 @@ 145.4 +local function map_2digit_year(y2) 145.5 + local current_year = atom.date:get_current().year 145.6 + local guess2 = math.floor(current_year / 100) * 100 + tonumber(y2) 145.7 + local guess1 = guess2 - 100 145.8 + local guess3 = guess2 + 100 145.9 + if guess1 >= current_year - 80 and guess1 <= current_year + 10 then 145.10 + return guess1 145.11 + elseif guess2 >= current_year - 80 and guess2 <= current_year + 10 then 145.12 + return guess2 145.13 + elseif guess3 >= current_year - 80 and guess3 <= current_year + 10 then 145.14 + return guess3 145.15 + end 145.16 +end 145.17 + 145.18 +function parse.date(str, dest_type, options) 145.19 + if dest_type ~= atom.date then 145.20 + error("parse.date(...) can only return dates, but a different destination type than atom.date was given.") 145.21 + end 145.22 + local date_format = locale.get("date_format") 145.23 + if date_format and string.find(date_format, "Y+%-D+%-M+") then 145.24 + error("Date format collision with ISO standard.") 145.25 + end 145.26 + if string.match(str, "^%s*$") then 145.27 + return nil 145.28 + end 145.29 + -- first try ISO format 145.30 + local year, month, day = string.match( 145.31 + str, "^%s*([0-9][0-9][0-9][0-9])%-([0-9][0-9])%-([0-9][0-9])%s*$" 145.32 + ) 145.33 + if year then 145.34 + return atom.date{ 145.35 + year = tonumber(year), 145.36 + month = tonumber(month), 145.37 + day = tonumber(day) 145.38 + } 145.39 + end 145.40 + if not date_format then 145.41 + return atom.date.invalid 145.42 + end 145.43 + local format_parts = {} 145.44 + local numeric_parts = {} 145.45 + for part in string.gmatch(date_format, "[YMD]+") do 145.46 + format_parts[#format_parts+1] = part 145.47 + end 145.48 + for part in string.gmatch(str, "[0-9]+") do 145.49 + numeric_parts[#numeric_parts+1] = part 145.50 + end 145.51 + if #format_parts ~= #numeric_parts then 145.52 + return atom.date.invalid 145.53 + end 145.54 + local year, month, day 145.55 + local function process_part(format_part, numeric_part) 145.56 + if string.find(format_part, "^Y+$") then 145.57 + if #numeric_part == 4 then 145.58 + year = tonumber(numeric_part) 145.59 + elseif #numeric_part == 2 then 145.60 + year = map_2digit_year(numeric_part) 145.61 + else 145.62 + return atom.date.invalid 145.63 + end 145.64 + elseif string.find(format_part, "^M+$") then 145.65 + month = tonumber(numeric_part) 145.66 + elseif string.find(format_part, "^D+$") then 145.67 + day = tonumber(numeric_part) 145.68 + else 145.69 + if not #format_part == #numeric_part then 145.70 + return atom.date.invalid 145.71 + end 145.72 + local year_str = "" 145.73 + local month_str = "" 145.74 + local day_str = "" 145.75 + for i = 1, #format_part do 145.76 + local format_char = string.sub(format_part, i, i) 145.77 + local number_char = string.sub(numeric_part, i, i) 145.78 + if format_char == "Y" then 145.79 + year_str = year_str .. number_char 145.80 + elseif format_char == "M" then 145.81 + month_str = month_str .. number_char 145.82 + elseif format_char == "D" then 145.83 + day_str = day_str .. number_char 145.84 + else 145.85 + error("Assertion failed.") 145.86 + end 145.87 + end 145.88 + if #year_str == 2 then 145.89 + year = map_2digit_year(year_str) 145.90 + else 145.91 + year = tonumber(year_str) 145.92 + end 145.93 + month = tonumber(month_str) 145.94 + day = tonumber(day_str) 145.95 + end 145.96 + end 145.97 + for i = 1, #format_parts do 145.98 + process_part(format_parts[i], numeric_parts[i]) 145.99 + end 145.100 + if not year or not month or not day then 145.101 + error("Date parser did not determine year, month and day. Maybe the 'date_format' locale is erroneous?") 145.102 + end 145.103 + return atom.date{ year = year, month = month, day = day } 145.104 +end
146.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 146.2 +++ b/framework/env/parse/decimal.lua Sun Oct 25 12:00:00 2009 +0100 146.3 @@ -0,0 +1,75 @@ 146.4 +local digit_set = { 146.5 + ["0"] = true, ["1"] = true, ["2"] = true, ["3"] = true, ["4"] = true, 146.6 + ["5"] = true, ["6"] = true, ["7"] = true, ["8"] = true, ["9"] = true 146.7 +} 146.8 + 146.9 +function parse.decimal(str, dest_type, options) 146.10 + local str = parse._pre_fold(str) 146.11 + local dest_type = dest_type or atom.number 146.12 + local options = options or {} 146.13 + if str == "" then 146.14 + return nil 146.15 + else 146.16 + local decimal_shift = options.decimal_shift or 0 146.17 + if decimal_shift == true then 146.18 + decimal_shift = options.precision 146.19 + end 146.20 + local decimal_point = locale.get("decimal_point") or "." 146.21 + local negative = nil 146.22 + local int = 0 146.23 + local frac = 0 146.24 + local precision = 0 146.25 + local after_point = false 146.26 + for char in string.gmatch(str, ".") do 146.27 + local skip = false 146.28 + if negative == nil then 146.29 + if char == "+" then 146.30 + negative = false 146.31 + skip = true 146.32 + elseif char == "-" then -- real minus sign already replaced by _pre_fold 146.33 + negative = true 146.34 + skip = true 146.35 + end 146.36 + end 146.37 + if not skip then 146.38 + if digit_set[char] then 146.39 + if after_point then 146.40 + if decimal_shift > 0 then 146.41 + int = 10 * int + tonumber(char) 146.42 + decimal_shift = decimal_shift - 1 146.43 + else 146.44 + frac = 10 * frac + tonumber(char) 146.45 + precision = precision + 1 146.46 + end 146.47 + else 146.48 + int = 10 * int + tonumber(char) 146.49 + end 146.50 + elseif char == decimal_point then 146.51 + if after_point then 146.52 + return dest_type.invalid 146.53 + else 146.54 + after_point = true 146.55 + end 146.56 + elseif char ~= " " then -- TODO: ignore thousand seperator too, when supported by format.decimal 146.57 + return dest_type.invalid 146.58 + end 146.59 + end 146.60 + end 146.61 + int = int * 10 ^ decimal_shift 146.62 + if dest_type == atom.number or dest_type == atom.integer then 146.63 + if dest_type == atom.integer and frac ~= 0 then 146.64 + return atom.not_a_number 146.65 + else 146.66 + local f = int + frac / 10 ^ precision 146.67 + if negative then 146.68 + f = -f 146.69 + end 146.70 + return f 146.71 + end 146.72 + elseif dest_type == atom.fraction then 146.73 + return atom.fraction(int * 10 ^ precision + frac, 10 ^ precision) 146.74 + else 146.75 + error("Missing or invalid destination type for parsing.") 146.76 + end 146.77 + end 146.78 +end
147.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 147.2 +++ b/framework/env/parse/percentage.lua Sun Oct 25 12:00:00 2009 +0100 147.3 @@ -0,0 +1,30 @@ 147.4 +function parse.percentage(str, dest_type, options) 147.5 + local str = parse._pre_fold(str) 147.6 + local dest_type = dest_type or atom.number 147.7 + local options = table.new(options) 147.8 + options.precision = options.precision or 0 147.9 + if options.decimal_shift == true then 147.10 + options.decimal_shift = options.precision + 2 147.11 + end 147.12 + local f = parse.decimal(string.match(str, "^ *([^%%]*) *%%? *$"), dest_type, options) 147.13 + if dest_type == atom.number then 147.14 + if f then 147.15 + return f / 100 147.16 + end 147.17 + elseif dest_type == atom.integer then 147.18 + if f then 147.19 + f = f / 100 147.20 + if atom.is_integer(f) then 147.21 + return f 147.22 + else 147.23 + return atom.integer.invalid 147.24 + end 147.25 + end 147.26 + elseif dest_type == atom.fraction then 147.27 + if f then 147.28 + return f / 100 147.29 + end 147.30 + else 147.31 + error("Missing or invalid destination type for parsing.") 147.32 + end 147.33 +end
148.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 148.2 +++ b/framework/env/parse/time.lua Sun Oct 25 12:00:00 2009 +0100 148.3 @@ -0,0 +1,3 @@ 148.4 +function parse.time(str, dest_type, options) 148.5 + error("Not implemented.") -- TODO 148.6 +end
149.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 149.2 +++ b/framework/env/parse/timestamp.lua Sun Oct 25 12:00:00 2009 +0100 149.3 @@ -0,0 +1,3 @@ 149.4 +function parse.timestamp(str, dest_type, options) 149.5 + error("Not implemented.") -- TODO 149.6 +end
150.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 150.2 +++ b/framework/env/request/__init.lua Sun Oct 25 12:00:00 2009 +0100 150.3 @@ -0,0 +1,31 @@ 150.4 +request._status = nil 150.5 +request._forward = nil 150.6 +request._forward_processed = false 150.7 +request._redirect = nil 150.8 +request._absolute_baseurl = nil 150.9 +request._404_route = nil 150.10 +request._force_absolute_baseurl = false 150.11 +request._perm_params = {} 150.12 +request._csrf_secret = nil 150.13 + 150.14 +local depth 150.15 +if cgi then -- if-clause to support interactive mode 150.16 + depth = tonumber(cgi.params._webmcp_urldepth) 150.17 +end 150.18 +if depth and depth > 0 then 150.19 + local elements = {} 150.20 + for i = 1, depth do 150.21 + elements[#elements+1] = "../" 150.22 + end 150.23 + request._relative_baseurl = table.concat(elements) 150.24 +else 150.25 + request._relative_baseurl = "./" 150.26 +end 150.27 + 150.28 +request._app_basepath = assert( 150.29 + os.getenv("WEBMCP_APP_BASEPATH"), 150.30 + 'WEBMCP_APP_BASEPATH is not set.' 150.31 +) 150.32 +if not string.find(request._app_basepath, "/$") then 150.33 + request._app_basebase = request._app_basepath .. "/" 150.34 +end
151.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 151.2 +++ b/framework/env/request/force_absolute_baseurl.lua Sun Oct 25 12:00:00 2009 +0100 151.3 @@ -0,0 +1,10 @@ 151.4 +--[[-- 151.5 +request.force_absolute_baseurl() 151.6 + 151.7 +Calling this function causes subsequent calls of request.get_relative_baseurl() to return absolute URLs instead. 151.8 + 151.9 +--]]-- 151.10 + 151.11 +function request.force_absolute_baseurl() 151.12 + request._force_absolute_baseurl = true 151.13 +end
152.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 152.2 +++ b/framework/env/request/forward.lua Sun Oct 25 12:00:00 2009 +0100 152.3 @@ -0,0 +1,17 @@ 152.4 +--[[-- 152.5 +request.forward{ 152.6 + module = module, -- module name 152.7 + view = view -- view name 152.8 +} 152.9 + 152.10 +This function is called automatically to forward to another view, after an action and all its filters have finished execution, if routing mode "forward" has been chosen. Calling request.forward{...} (or request.redirect{...}) explicitly inside an action will cause routing information from the browser to be ignored. Calling request.forward{...} causes all GET/POST parameters of the action to be preserved for the given view. 152.11 + 152.12 +--]]-- 152.13 + 152.14 +function request.forward(args) 152.15 + if request.is_rerouted() then 152.16 + error("Tried to forward after another forward or redirect.") 152.17 + end 152.18 + request._forward = args 152.19 + trace.forward { module = args.module, view = args.view } 152.20 +end
153.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 153.2 +++ b/framework/env/request/get_404_route.lua Sun Oct 25 12:00:00 2009 +0100 153.3 @@ -0,0 +1,11 @@ 153.4 +--[[-- 153.5 +route_info = -- table with 'module' and 'view' field 153.6 +request.get_404_route() 153.7 + 153.8 +Returns the data passed to a previous call of request.set_404_route{...}, or nil. 153.9 + 153.10 +--]]-- 153.11 + 153.12 +function request.get_404_route() 153.13 + return request._404_route 153.14 +end
154.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 154.2 +++ b/framework/env/request/get_absolute_baseurl.lua Sun Oct 25 12:00:00 2009 +0100 154.3 @@ -0,0 +1,7 @@ 154.4 +function request.get_absolute_baseurl() 154.5 + if request._absolute_baseurl then 154.6 + return request._absolute_baseurl 154.7 + else 154.8 + error("Absolute base URL is unknown. It should be set in the configuration by calling request.set_absolute_baseurl(...).") 154.9 + end 154.10 +end
155.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 155.2 +++ b/framework/env/request/get_action.lua Sun Oct 25 12:00:00 2009 +0100 155.3 @@ -0,0 +1,15 @@ 155.4 +--[[-- 155.5 +action_name = 155.6 +request.get_action() 155.7 + 155.8 +Returns the name of the currently requested action, or nil in case of a view. 155.9 + 155.10 +--]]-- 155.11 + 155.12 +function request.get_action() 155.13 + if request._forward_processed then 155.14 + return nil 155.15 + else 155.16 + return cgi.params._webmcp_action 155.17 + end 155.18 +end 155.19 \ No newline at end of file
156.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 156.2 +++ b/framework/env/request/get_app_basepath.lua Sun Oct 25 12:00:00 2009 +0100 156.3 @@ -0,0 +1,11 @@ 156.4 +--[[-- 156.5 +path = -- path to directory of application with trailing slash 156.6 +request.get_app_basepath() 156.7 + 156.8 +This function returns the path to the base directory of the application. A trailing slash is always included. 156.9 + 156.10 +--]]-- 156.11 + 156.12 +function request.get_app_basepath() 156.13 + return request._app_basepath 156.14 +end
157.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 157.2 +++ b/framework/env/request/get_app_name.lua Sun Oct 25 12:00:00 2009 +0100 157.3 @@ -0,0 +1,11 @@ 157.4 +--[[-- 157.5 +app_name = 157.6 +request.get_app_name() 157.7 + 157.8 +Returns the application name set by the environment variable 'WEBMCP_APP_NAME', or "main" as default application name, if the environment variable is unset. 157.9 + 157.10 +--]]-- 157.11 + 157.12 +function request.get_app_name() 157.13 + return os.getenv("WEBMCP_APP_NAME") or 'main' 157.14 +end
158.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 158.2 +++ b/framework/env/request/get_config_name.lua Sun Oct 25 12:00:00 2009 +0100 158.3 @@ -0,0 +1,11 @@ 158.4 +--[[-- 158.5 +config_name = 158.6 +request.get_config_name() 158.7 + 158.8 +Returns the name of the configuration selected by the environment variable 'WEBMCP_CONFIG_NAME', or nil if the environment variable is not set. 158.9 + 158.10 +--]]-- 158.11 + 158.12 +function request.get_config_name() 158.13 + return os.getenv("WEBMCP_CONFIG_NAME") 158.14 +end 158.15 \ No newline at end of file
159.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 159.2 +++ b/framework/env/request/get_csrf_secret.lua Sun Oct 25 12:00:00 2009 +0100 159.3 @@ -0,0 +1,11 @@ 159.4 +--[[-- 159.5 +secret = -- secret string, previously set with request.set_csrf_secret(...) 159.6 +request.get_csrf_secret() 159.7 + 159.8 +Returns the secret string being previously set with request.set_csrf_secret(...) for inclusion in web forms (nil if none is set). This function is automatically used by the ui.form{...} helper. 159.9 + 159.10 +--]]-- 159.11 + 159.12 +function request.get_csrf_secret(secret) 159.13 + return request._csrf_secret 159.14 +end
160.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 160.2 +++ b/framework/env/request/get_module.lua Sun Oct 25 12:00:00 2009 +0100 160.3 @@ -0,0 +1,15 @@ 160.4 +--[[-- 160.5 +module_name = 160.6 +request.get_module() 160.7 + 160.8 +Returns the name of the module of the currently requested view or action. 160.9 + 160.10 +--]]-- 160.11 + 160.12 +function request.get_module() 160.13 + if request._forward_processed then 160.14 + return request._forward.module or cgi.params._webmcp_module or 'index' 160.15 + else 160.16 + return cgi.params._webmcp_module or 'index' 160.17 + end 160.18 +end
161.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 161.2 +++ b/framework/env/request/get_perm_params.lua Sun Oct 25 12:00:00 2009 +0100 161.3 @@ -0,0 +1,12 @@ 161.4 +--[[-- 161.5 +perm_params = -- table containing permanent parameters 161.6 +request.get_perm_params() 161.7 + 161.8 +This function returns a table containing all permanent paremeters set with request.set_perm_param(...). Modifications of the returned table have no side effects. 161.9 + 161.10 +--]]-- 161.11 + 161.12 +function request.get_perm_params() 161.13 + -- NOTICE: it's important to return a copy here 161.14 + return table.new(request._perm_params) 161.15 +end
162.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 162.2 +++ b/framework/env/request/get_redirect_data.lua Sun Oct 25 12:00:00 2009 +0100 162.3 @@ -0,0 +1,11 @@ 162.4 +--[[-- 162.5 +redirect_data = 162.6 +request.get_redirect_data() 162.7 + 162.8 +This function returns redirect information from a previous call of request.redirect{...}, or nil if no redirect was requested. 162.9 + 162.10 +--]]-- 162.11 + 162.12 +function request.get_redirect_data() 162.13 + return request._redirect 162.14 +end
163.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 163.2 +++ b/framework/env/request/get_relative_baseurl.lua Sun Oct 25 12:00:00 2009 +0100 163.3 @@ -0,0 +1,15 @@ 163.4 +--[[-- 163.5 +baseurl = 163.6 +request.get_relative_baseurl() 163.7 + 163.8 +This function returns a relative base URL of the application. If request.force_absolute_baseurl() has been called before, an absolute URL is returned. 163.9 + 163.10 +--]]-- 163.11 + 163.12 +function request.get_relative_baseurl() 163.13 + if request._force_absolute_baseurl then 163.14 + return (request.get_absolute_baseurl()) 163.15 + else 163.16 + return request._relative_baseurl 163.17 + end 163.18 +end
164.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 164.2 +++ b/framework/env/request/get_status.lua Sun Oct 25 12:00:00 2009 +0100 164.3 @@ -0,0 +1,11 @@ 164.4 +--[[-- 164.5 +status_string = 164.6 +request.get_status() 164.7 + 164.8 +Returns a HTTP status previously set with request.set_status(...). 164.9 + 164.10 +--]]-- 164.11 + 164.12 +function request.get_status() 164.13 + return request._status 164.14 +end
165.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 165.2 +++ b/framework/env/request/get_view.lua Sun Oct 25 12:00:00 2009 +0100 165.3 @@ -0,0 +1,26 @@ 165.4 +--[[-- 165.5 +view_name = 165.6 +request.get_view() 165.7 + 165.8 +Returns the name of the currently requested view, or nil in case of an action. 165.9 + 165.10 +--]]-- 165.11 + 165.12 +function request.get_view() 165.13 + if request._forward_processed then 165.14 + return request._forward.view or 'index' 165.15 + else 165.16 + if cgi.params._webmcp_view then 165.17 + local suffix = cgi.params._webmcp_suffix or "html" 165.18 + if suffix == "html" then 165.19 + return cgi.params._webmcp_view 165.20 + else 165.21 + return cgi.params._webmcp_view .. "." .. suffix 165.22 + end 165.23 + elseif not cgi.params._webmcp_action then 165.24 + return 'index' 165.25 + else 165.26 + return nil 165.27 + end 165.28 + end 165.29 +end
166.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 166.2 +++ b/framework/env/request/is_ajax.lua Sun Oct 25 12:00:00 2009 +0100 166.3 @@ -0,0 +1,3 @@ 166.4 +function request.is_ajax() 166.5 + return cgi.params['ajax'] == '1' 166.6 +end 166.7 \ No newline at end of file
167.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 167.2 +++ b/framework/env/request/is_post.lua Sun Oct 25 12:00:00 2009 +0100 167.3 @@ -0,0 +1,15 @@ 167.4 +--[[-- 167.5 +bool = -- true, if the current request is a POST request 167.6 +request.is_post() 167.7 + 167.8 +This function can be used to check, if the current request is a POST request. 167.9 + 167.10 +--]]-- 167.11 + 167.12 +function request.is_post() 167.13 + if request._forward_processed then 167.14 + return false 167.15 + else 167.16 + return cgi.method == "POST" 167.17 + end 167.18 +end
168.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 168.2 +++ b/framework/env/request/is_rerouted.lua Sun Oct 25 12:00:00 2009 +0100 168.3 @@ -0,0 +1,20 @@ 168.4 +--[[-- 168.5 + 168.6 +bool = -- true, if request.forard{...} or request.redirect{...} has been called before. 168.7 +request_is_rerouted() 168.8 + 168.9 +This function returns true, if request.forward{...} or request.redirect{...} has been called before. In a new request caused by a redirect the function returns false. After a forward has been processed, this function also returns false. 168.10 + 168.11 +--]]-- 168.12 + 168.13 + 168.14 +function request.is_rerouted() 168.15 + if 168.16 + (request._forward and not request._forward_processed) or 168.17 + request._redirect 168.18 + then 168.19 + return true 168.20 + else 168.21 + return false 168.22 + end 168.23 +end
169.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 169.2 +++ b/framework/env/request/process_forward.lua Sun Oct 25 12:00:00 2009 +0100 169.3 @@ -0,0 +1,16 @@ 169.4 +--[[-- 169.5 +request.process_forward() 169.6 + 169.7 +This function causes a previous call of request.forward{...} to be in effect. It is called automatically when neccessary, and must not be called explicitly by any application. 169.8 + 169.9 +--]]-- 169.10 + 169.11 +function request.process_forward() 169.12 + if request._forward then 169.13 + request._forward_processed = true 169.14 + trace.request{ 169.15 + module = request.get_module(), 169.16 + view = request.get_view() 169.17 + } 169.18 + end 169.19 +end
170.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 170.2 +++ b/framework/env/request/redirect.lua Sun Oct 25 12:00:00 2009 +0100 170.3 @@ -0,0 +1,39 @@ 170.4 +--[[-- 170.5 +request.redirect{ 170.6 + module = module, -- module name 170.7 + view = view, -- view name 170.8 + id = id, -- optional id for view 170.9 + params = params -- optional view parameters 170.10 +} 170.11 + 170.12 +Calling this function causes the WebMCP to do a 303 HTTP redirect after the current view or action and all filters have finished execution. If routing mode "redirect" has been chosen, then this function is called automatically after an action and all its filters have finished execution. Calling request.redirect{...} (or request.forward{...}) explicitly inside an action will cause routing information from the browser to be ignored. To preserve GET/POST parameters of an action, use request.forward{...} instead. Currently no redirects to external (absolute) URLs are possible, there will be an implementation in future though. 170.13 + 170.14 +--]]-- 170.15 + 170.16 +function request.redirect(args) 170.17 + -- TODO: support redirects to external URLs too 170.18 + -- (needs fixes in the trace system as well) 170.19 + local module = args.module 170.20 + local view = args.view 170.21 + local id = args.id 170.22 + local params = args.params or {} 170.23 + if type(module) ~= "string" then 170.24 + error("No module string passed to request.redirect{...}.") 170.25 + end 170.26 + if type(view) ~= "string" then 170.27 + error("No view string passed to request.redirect{...}.") 170.28 + end 170.29 + if type(params) ~= "table" then 170.30 + error("Params array passed to request.redirect{...} is not a table.") 170.31 + end 170.32 + if request.is_rerouted() then 170.33 + error("Tried to redirect after another forward or redirect.") 170.34 + end 170.35 + request._redirect = { 170.36 + module = module, 170.37 + view = view, 170.38 + id = id, 170.39 + params = params 170.40 + } 170.41 + trace.redirect{ module = args.module, view = args.view } 170.42 +end
171.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 171.2 +++ b/framework/env/request/set_404_route.lua Sun Oct 25 12:00:00 2009 +0100 171.3 @@ -0,0 +1,13 @@ 171.4 +--[[-- 171.5 +request.set_404_route{ 171.6 + module = module, -- module name 171.7 + view = view -- view name 171.8 +} 171.9 + 171.10 +In case a view or action is not found, the given module and view will be used to display an error page. 171.11 + 171.12 +--]]-- 171.13 + 171.14 +function request.set_404_route(tbl) 171.15 + request._404_route = tbl 171.16 +end
172.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 172.2 +++ b/framework/env/request/set_absolute_baseurl.lua Sun Oct 25 12:00:00 2009 +0100 172.3 @@ -0,0 +1,16 @@ 172.4 +--[[-- 172.5 +request.set_absolute_baseurl( 172.6 + url -- Base URL of the application 172.7 +) 172.8 + 172.9 +Calling this function is neccessary for every configuration, because an absolute URL is needed for HTTP redirects. If the URL of the application is volatile, and if you don't bother violating the HTTP standard, you might want to execute request.set_absolute_baseurl(request.get_relative_baseurl()) in your application configuration. 172.10 + 172.11 +--]]-- 172.12 + 172.13 +function request.set_absolute_baseurl(url) 172.14 + if string.find(url, "/$") then 172.15 + request._absolute_baseurl = url 172.16 + else 172.17 + request._absolute_baseurl = url .. "/" 172.18 + end 172.19 +end
173.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 173.2 +++ b/framework/env/request/set_csrf_secret.lua Sun Oct 25 12:00:00 2009 +0100 173.3 @@ -0,0 +1,18 @@ 173.4 +--[[-- 173.5 +request.set_csrf_secret( 173.6 + secret -- secret random string 173.7 +) 173.8 + 173.9 +Sets a secret string to be used as protection against cross-site request forgery attempts. This string will be transmitted to each action via a hidden form field named "_webmcp_csrf_secret". If this function is called during an action, and there is no CGI GET/POST parameter "_webmcp_csrf_secret" already being set to the given secret, then an error will be thrown to prohibit execution of the action. 173.10 + 173.11 +--]]-- 173.12 + 173.13 +function request.set_csrf_secret(secret) 173.14 + if 173.15 + request.get_action() and 173.16 + cgi.params._webmcp_csrf_secret ~= secret 173.17 + then 173.18 + error("Cross-Site Request Forgery attempt detected"); 173.19 + end 173.20 + request._csrf_secret = secret 173.21 +end
174.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 174.2 +++ b/framework/env/request/set_perm_param.lua Sun Oct 25 12:00:00 2009 +0100 174.3 @@ -0,0 +1,13 @@ 174.4 +--[[-- 174.5 +request.set_perm_param( 174.6 + key, -- name of parameter 174.7 + value -- value of parameter 174.8 +) 174.9 + 174.10 +Setting a so-called "permanent parameter" with this function will cause a key value pair to be included in every HTTP link inside the application. It is used as a cookie replacement, where cookies are not suitable, e.g. because you want multiple browser windows to not interfere with each other. 174.11 + 174.12 +--]]-- 174.13 + 174.14 +function request.set_perm_param(key, value) 174.15 + request._perm_params[key] = value 174.16 +end
175.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 175.2 +++ b/framework/env/request/set_status.lua Sun Oct 25 12:00:00 2009 +0100 175.3 @@ -0,0 +1,22 @@ 175.4 +--[[-- 175.5 +request.set_status( 175.6 + str -- string containing a HTTP status code, e.g. "404 Not Found" 175.7 +) 175.8 + 175.9 +Calling this function causes a HTTP status different from 200 OK (or in case of error different from 500 Internal Server Error) to be sent to the browser. 175.10 + 175.11 +--]]-- 175.12 + 175.13 +function request.set_status(str) 175.14 + if str then 175.15 + local t = type(str) 175.16 + if type(str) == "number" then 175.17 + str = tostring(str) 175.18 + elseif type(str) ~= "string" then 175.19 + error("request.set_status(...) must be called with a string as parameter.") 175.20 + end 175.21 + request._status = str 175.22 + else 175.23 + request._status = nil 175.24 + end 175.25 +end
176.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 176.2 +++ b/framework/env/slot/__init.lua Sun Oct 25 12:00:00 2009 +0100 176.3 @@ -0,0 +1,10 @@ 176.4 +slot._data_metatable = {} 176.5 +function slot._data_metatable:__index(key) 176.6 + self[key] = { string_fragments = {}, state_table = {} } 176.7 + return self[key] 176.8 +end 176.9 +slot._active_slot = 'default' 176.10 +slot._current_layout = 'default' 176.11 +slot._content_type = nil 176.12 + 176.13 +slot.reset_all()
177.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 177.2 +++ b/framework/env/slot/dump_all.lua Sun Oct 25 12:00:00 2009 +0100 177.3 @@ -0,0 +1,36 @@ 177.4 +--[[-- 177.5 +blob = -- string for later usage with slot.restore_all(...) 177.6 +slot.dump_all() 177.7 + 177.8 +Returns a single string, containing all slot contents. The result of this function can be used to restore all slots after a 303 redirect. This is done automatically by the WebMCP using slot.restore_all(...). If the result of this function is an empty string, then all slots are empty. 177.9 + 177.10 +--]]-- 177.11 + 177.12 +local function encode(str) 177.13 + return ( 177.14 + string.gsub( 177.15 + str, 177.16 + "[=;%[%]]", 177.17 + function(char) 177.18 + if char == "=" then return "[eq]" 177.19 + elseif char == ";" then return "[s]" 177.20 + elseif char == "[" then return "[o]" 177.21 + elseif char == "]" then return "[c]" 177.22 + else end 177.23 + end 177.24 + ) 177.25 + ) 177.26 +end 177.27 + 177.28 +function slot.dump_all() 177.29 + local blob_parts = {} 177.30 + for key in pairs(slot._data) do 177.31 + if type(key) == "string" then 177.32 + local value = slot.get_content(key) 177.33 + if value ~= "" then 177.34 + blob_parts[#blob_parts + 1] = encode(key) .. "=" .. encode(value) 177.35 + end 177.36 + end 177.37 + end 177.38 + return table.concat(blob_parts, ";") 177.39 +end
178.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 178.2 +++ b/framework/env/slot/get_content.lua Sun Oct 25 12:00:00 2009 +0100 178.3 @@ -0,0 +1,20 @@ 178.4 +--[[-- 178.5 +content = 178.6 +slot.get_content( 178.7 + slot_ident -- name of the slot 178.8 +) 178.9 + 178.10 +This function returns the content of a chosen slot as a single string. 178.11 + 178.12 +--]]-- 178.13 + 178.14 +function slot.get_content(slot_ident) 178.15 + local slot_data = slot._data[slot_ident] 178.16 + if #slot_data.string_fragments > 1 then 178.17 + local str = table.concat(slot_data.string_fragments) 178.18 + slot_data.string_fragments = { str } 178.19 + return str 178.20 + else 178.21 + return slot_data.string_fragments[1] or "" 178.22 + end 178.23 +end
179.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 179.2 +++ b/framework/env/slot/get_content_type.lua Sun Oct 25 12:00:00 2009 +0100 179.3 @@ -0,0 +1,11 @@ 179.4 +--[[-- 179.5 +content_type = -- content-type as selected with slot.set_layout(...) 179.6 +slot.get_content_type() 179.7 + 179.8 +This function returns the content-type to be sent to the browser. It may be changed by calling slot.set_layout(...). The default content-type is "text/html; charset=UTF-8". 179.9 + 179.10 +--]]-- 179.11 + 179.12 +function slot.get_content_type() 179.13 + return slot._content_type or 'text/html; charset=UTF-8' 179.14 +end
180.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 180.2 +++ b/framework/env/slot/get_state_table.lua Sun Oct 25 12:00:00 2009 +0100 180.3 @@ -0,0 +1,11 @@ 180.4 +--[[-- 180.5 +state_table = -- table for saving a slot's state 180.6 +slot.get_state_table() 180.7 + 180.8 +This function returns a table, holding state information of the currently active slot. To change the state information the returned table may be modified. 180.9 + 180.10 +--]]-- 180.11 + 180.12 +function slot.get_state_table() 180.13 + return slot.get_state_table_of(slot._active_slot) 180.14 +end
181.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 181.2 +++ b/framework/env/slot/get_state_table_of.lua Sun Oct 25 12:00:00 2009 +0100 181.3 @@ -0,0 +1,13 @@ 181.4 +--[[-- 181.5 +state_table = -- table for saving the slot's state 181.6 +slot.get_state_table_of( 181.7 + slot_ident -- name of a slot 181.8 +) 181.9 + 181.10 +This function returns a table, holding state information of the named slot. To change the state information the returned table may be modified. 181.11 + 181.12 +--]]-- 181.13 + 181.14 +function slot.get_state_table_of(slot_ident) 181.15 + return slot._data[slot_ident].state_table 181.16 +end
182.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 182.2 +++ b/framework/env/slot/put.lua Sun Oct 25 12:00:00 2009 +0100 182.3 @@ -0,0 +1,17 @@ 182.4 +--[[-- 182.5 +slot.put( 182.6 + string1, -- string to be written into the active slot 182.7 + string2, -- another string to be written into the active slot 182.8 + ... 182.9 +) 182.10 + 182.11 +This function is used to write strings into the active slot. 182.12 + 182.13 +-- NOTE: ACCELERATED FUNCTION 182.14 +-- Do not change unless also you also update webmcp_accelerator.c 182.15 + 182.16 +--]]-- 182.17 + 182.18 +function slot.put(...) 182.19 + return slot.put_into(slot._active_slot, ...) 182.20 +end 182.21 \ No newline at end of file
183.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 183.2 +++ b/framework/env/slot/put_into.lua Sun Oct 25 12:00:00 2009 +0100 183.3 @@ -0,0 +1,23 @@ 183.4 +--[[-- 183.5 +slot.put( 183.6 + slot_ident -- name of a slot 183.7 + string1, -- string to be written into the named slot 183.8 + string2, -- another string to be written into the named slot 183.9 + ... 183.10 +) 183.11 + 183.12 +This function is used to write strings into a named slot. 183.13 + 183.14 +-- NOTE: ACCELERATED FUNCTION 183.15 +-- Do not change unless also you also update webmcp_accelerator.c 183.16 + 183.17 +--]]-- 183.18 + 183.19 +function slot.put_into(slot_ident, ...) 183.20 + local t = slot._data[slot_ident].string_fragments 183.21 + for i = 1, math.huge do 183.22 + local v = select(i, ...) 183.23 + if v == nil then break end 183.24 + t[#t + 1] = v 183.25 + end 183.26 +end 183.27 \ No newline at end of file
184.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 184.2 +++ b/framework/env/slot/render_layout.lua Sun Oct 25 12:00:00 2009 +0100 184.3 @@ -0,0 +1,48 @@ 184.4 +--[[-- 184.5 +output = -- document/data to be sent to the web browser 184.6 +slot.render_layout() 184.7 + 184.8 +This function returns the selected layout after replacing all slot placeholders with the respective slot contents. If slot.set_layout(...) was called with nil as first argument, then no layout will be used, but only the contents of the slot named "data" are returned. 184.9 + 184.10 +--]]-- 184.11 + 184.12 +function slot.render_layout() 184.13 + if slot._current_layout then 184.14 + local layout_file = assert(io.open( 184.15 + encode.file_path( 184.16 + request.get_app_basepath(), 184.17 + 'app', 184.18 + request.get_app_name(), 184.19 + '_layout', 184.20 + slot._current_layout .. '.html' 184.21 + ), 184.22 + 'r' 184.23 + )) 184.24 + local layout = layout_file:read("*a") 184.25 + io.close(layout_file) 184.26 + 184.27 + -- render layout 184.28 + layout = string.gsub(layout, "__BASEURL__/?", request.get_relative_baseurl()) -- TODO: find a better placeholder than __BASEURL__ ? 184.29 + layout = string.gsub(layout, '<!%-%- *WEBMCP +SLOT +([^ ]+) *%-%->', 184.30 + function(slot_ident) 184.31 + if #slot.get_content(slot_ident) > 0 then 184.32 + return '<div class="slot_' .. slot_ident .. '" id="slot_' .. slot_ident .. '">' .. slot.get_content(slot_ident).. '</div>' 184.33 + else 184.34 + return '' 184.35 + end 184.36 + end 184.37 + ) 184.38 + layout = string.gsub(layout, '<!%-%- *WEBMCP +SLOTNODIV +([^ ]+) *%-%->', 184.39 + function(slot_ident) 184.40 + if #slot.get_content(slot_ident) > 0 then 184.41 + return slot.get_content(slot_ident) 184.42 + else 184.43 + return '' 184.44 + end 184.45 + end 184.46 + ) 184.47 + return layout 184.48 + else 184.49 + return slot.get_content("data") 184.50 + end 184.51 +end
185.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 185.2 +++ b/framework/env/slot/reset.lua Sun Oct 25 12:00:00 2009 +0100 185.3 @@ -0,0 +1,12 @@ 185.4 +--[[-- 185.5 +slot.reset( 185.6 + slot_ident -- name of a slot to be emptied 185.7 +) 185.8 + 185.9 +Calling this function reset the named slot to be empty. 185.10 + 185.11 +--]]-- 185.12 + 185.13 +function slot.reset(slot_ident) 185.14 + slot._data[slot_ident] = nil 185.15 +end
186.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 186.2 +++ b/framework/env/slot/reset_all.lua Sun Oct 25 12:00:00 2009 +0100 186.3 @@ -0,0 +1,10 @@ 186.4 +--[[-- 186.5 +slot.reset_all() 186.6 + 186.7 +Calling this function resets all slots to be empty. 186.8 + 186.9 +--]]-- 186.10 + 186.11 +function slot.reset_all() 186.12 + slot._data = setmetatable({}, slot._data_metatable) 186.13 +end
187.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 187.2 +++ b/framework/env/slot/restore_all.lua Sun Oct 25 12:00:00 2009 +0100 187.3 @@ -0,0 +1,32 @@ 187.4 +--[[-- 187.5 +slot.restore_all( 187.6 + blob -- string as returned by slot.dump_all() 187.7 +) 187.8 + 187.9 +Restores all slots using a string created by slot.dump_all(). 187.10 + 187.11 +--]]-- 187.12 + 187.13 +local function decode(str) 187.14 + return ( 187.15 + string.gsub( 187.16 + str, 187.17 + "%[[a-z]+%]", 187.18 + function(char) 187.19 + if char == "[eq]" then return "=" 187.20 + elseif char == "[s]" then return ";" 187.21 + elseif char == "[o]" then return "[" 187.22 + elseif char == "[c]" then return "]" 187.23 + else end 187.24 + end 187.25 + ) 187.26 + ) 187.27 +end 187.28 + 187.29 +function slot.restore_all(blob) 187.30 + slot.reset_all() 187.31 + for encoded_key, encoded_value in string.gmatch(blob, "([^=;]*)=([^=;]*)") do 187.32 + local key, value = decode(encoded_key), decode(encoded_value) 187.33 + slot._data[key].string_fragments = { value } 187.34 + end 187.35 +end
188.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 188.2 +++ b/framework/env/slot/select.lua Sun Oct 25 12:00:00 2009 +0100 188.3 @@ -0,0 +1,18 @@ 188.4 +--[[-- 188.5 +slot.select( 188.6 + slot_ident, -- name of a slot 188.7 + function() 188.8 + ... -- code to be executed using the named slot 188.9 + end 188.10 +) 188.11 + 188.12 +This function executes code in a way that slot.put(...) and other functions write into the slot with the given name. Calls of slot.select may be nested. 188.13 + 188.14 +--]]-- 188.15 + 188.16 +function slot.select(slot_ident, block) 188.17 + local old_slot = slot._active_slot 188.18 + slot._active_slot = slot_ident 188.19 + block() 188.20 + slot._active_slot = old_slot 188.21 +end
189.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 189.2 +++ b/framework/env/slot/set_layout.lua Sun Oct 25 12:00:00 2009 +0100 189.3 @@ -0,0 +1,14 @@ 189.4 +--[[-- 189.5 +slot.set_layout( 189.6 + layout_ident, -- name of layout or nil for binary data in slot named "data" 189.7 + content_type -- content-type to be sent to the browser, or nil for default 189.8 +) 189.9 + 189.10 +This function selects which layout should be used when calling slot.render_layout(). If no layout is selected by passing nil as first argument, then no layout will be used, and the slot named "data" is used plainly. The second argument to slot.set_layout is the content-type which is sent to the browser. 189.11 + 189.12 +--]]-- 189.13 + 189.14 +function slot.set_layout(layout_ident, content_type) 189.15 + slot._current_layout = layout_ident 189.16 + slot._content_type = content_type 189.17 +end
190.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 190.2 +++ b/framework/env/slot/use_temporary.lua Sun Oct 25 12:00:00 2009 +0100 190.3 @@ -0,0 +1,22 @@ 190.4 +--[[-- 190.5 +slot_content = 190.6 +slot.use_temporary( 190.7 + function() 190.8 + ... 190.9 + end 190.10 +) 190.11 + 190.12 +This function creates a temporary slot and executes code in a way that slot.put(...) and other functions will write into the temporary slot. Afterwards the contents of the temporary slot are returned as a single string. 190.13 + 190.14 +--]]-- 190.15 + 190.16 +function slot.use_temporary(block) 190.17 + local old_slot = slot._active_slot 190.18 + local temp_slot_reference = {} -- just a unique reference 190.19 + slot._active_slot = temp_slot_reference 190.20 + block() 190.21 + slot._active_slot = old_slot 190.22 + local result = slot.get_content(temp_slot_reference) 190.23 + slot.reset(temp_slot_reference) 190.24 + return result 190.25 +end
191.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 191.2 +++ b/framework/env/tempstore/pop.lua Sun Oct 25 12:00:00 2009 +0100 191.3 @@ -0,0 +1,21 @@ 191.4 +--[[-- 191.5 +blob = -- loaded string 191.6 +tempstore.pop( 191.7 + key -- key as returned by tempstore.save(...) 191.8 +) 191.9 + 191.10 +This function restores data, which had been stored temporarily by tempstore.save(...). After loading the data, it is deleted from the tempstore automatically. 191.11 + 191.12 +--]]-- 191.13 + 191.14 +function tempstore.pop(key) 191.15 + local filename = encode.file_path( 191.16 + request.get_app_basepath(), 'tmp', "tempstore-" .. key .. ".tmp" 191.17 + ) 191.18 + local file = io.open(filename, "r") 191.19 + if not file then return nil end 191.20 + local blob = file:read("*a") 191.21 + io.close(file) 191.22 + os.remove(filename) 191.23 + return blob 191.24 +end
192.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 192.2 +++ b/framework/env/tempstore/save.lua Sun Oct 25 12:00:00 2009 +0100 192.3 @@ -0,0 +1,20 @@ 192.4 +--[[-- 192.5 +key = -- key to be used with tempstore.pop(...) to regain the stored string 192.6 +tempstore.save( 192.7 + blob -- string to be stored 192.8 +) 192.9 + 192.10 +This function stores data temporarily. It is used to restore slot information after a 303 HTTP redirect. It returns a key, which can be passed to tempstore.pop(...) to regain the stored data. 192.11 + 192.12 +--]]-- 192.13 + 192.14 +function tempstore.save(blob) 192.15 + local key = multirand.string(26, "123456789bcdfghjklmnpqrstvwxyz"); 192.16 + local filename = encode.file_path( 192.17 + request.get_app_basepath(), 'tmp', "tempstore-" .. key .. ".tmp" 192.18 + ) 192.19 + local file = assert(io.open(filename, "w")) 192.20 + file:write(blob) 192.21 + io.close(file) 192.22 + return key 192.23 +end 192.24 \ No newline at end of file
193.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 193.2 +++ b/framework/env/trace/__init.lua Sun Oct 25 12:00:00 2009 +0100 193.3 @@ -0,0 +1,2 @@ 193.4 +trace._tree = { type = "root" } 193.5 +trace._stack = { trace._tree }
194.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 194.2 +++ b/framework/env/trace/_close_section.lua Sun Oct 25 12:00:00 2009 +0100 194.3 @@ -0,0 +1,9 @@ 194.4 +function trace._close_section() 194.5 + local pos = #trace._stack 194.6 + local closed_section = trace._stack[pos] 194.7 + if not closed_section then 194.8 + error("All trace sections have been closed already.") 194.9 + end 194.10 + trace._stack[pos] = nil 194.11 + return closed_section 194.12 +end
195.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 195.2 +++ b/framework/env/trace/_new_entry.lua Sun Oct 25 12:00:00 2009 +0100 195.3 @@ -0,0 +1,5 @@ 195.4 +function trace._new_entry(node) 195.5 + local node = node or {} 195.6 + table.insert(trace._stack[#trace._stack], node) 195.7 + return node 195.8 +end
196.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 196.2 +++ b/framework/env/trace/_open_section.lua Sun Oct 25 12:00:00 2009 +0100 196.3 @@ -0,0 +1,6 @@ 196.4 +function trace._open_section(node) 196.5 + local node = trace._new_entry(node) 196.6 + node.section = true 196.7 + table.insert(trace._stack, node) 196.8 + return node 196.9 +end
197.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 197.2 +++ b/framework/env/trace/_render_sub_tree.lua Sun Oct 25 12:00:00 2009 +0100 197.3 @@ -0,0 +1,154 @@ 197.4 +local function open(class) 197.5 + slot.put('<li class="trace_' .. class .. '">') 197.6 +end 197.7 +local function open_head() 197.8 + slot.put('<div class="trace_head">') 197.9 +end 197.10 +local function close_head() 197.11 + slot.put('</div>') 197.12 +end 197.13 +local function close() 197.14 + slot.put('</li>') 197.15 +end 197.16 +function trace._render_sub_tree(node) 197.17 + local function render_children(tail) 197.18 + if #node > 0 then 197.19 + slot.put('<ul class="trace_list">') 197.20 + for idx, child in ipairs(node) do 197.21 + trace._render_sub_tree(child) 197.22 + end 197.23 + if tail then 197.24 + slot.put(tail) 197.25 + end 197.26 + slot.put("</ul>") 197.27 + end 197.28 + end 197.29 + local function close_with_children() 197.30 + close_head() 197.31 + render_children() 197.32 + close() 197.33 + end 197.34 + local node_type = node.type 197.35 + if node_type == "root" then 197.36 + render_children() 197.37 + elseif node_type == "debug" then 197.38 + open("debug") 197.39 + slot.put(encode.html(node.message)) 197.40 + close() 197.41 + elseif node_type == "request" then 197.42 + open("request") 197.43 + open_head() 197.44 + slot.put("REQUESTED") 197.45 + if node.view then 197.46 + slot.put(" VIEW") 197.47 + elseif node.action then 197.48 + slot.put(" ACTION") 197.49 + end 197.50 + slot.put( 197.51 + ": ", 197.52 + encode.html(node.module), 197.53 + "/", 197.54 + encode.html(node.view or node.action) 197.55 + ) 197.56 + close_with_children() 197.57 + elseif node_type == "config" then 197.58 + open("config") 197.59 + open_head() 197.60 + slot.put('Configuration "', encode.html(node.name), '"') 197.61 + close_with_children() 197.62 + elseif node_type == "filter" then 197.63 + open("filter") 197.64 + open_head() 197.65 + slot.put(encode.html(node.path)) 197.66 + close_with_children() 197.67 + elseif node_type == "view" then 197.68 + open("view") 197.69 + open_head() 197.70 + slot.put( 197.71 + "EXECUTE VIEW: ", 197.72 + encode.html(node.module), 197.73 + "/", 197.74 + encode.html(node.view) 197.75 + ) 197.76 + close_with_children() 197.77 + elseif node_type == "action" then 197.78 + if 197.79 + node.status and ( 197.80 + node.status == "ok" or 197.81 + string.find(node.status, "^ok_") 197.82 + ) 197.83 + then 197.84 + open("action_success") 197.85 + elseif 197.86 + node.status and ( 197.87 + node.status == "error" or 197.88 + string.find(node.status, "^error_") 197.89 + ) 197.90 + then 197.91 + open("action_softfail") 197.92 + else 197.93 + open("action_neutral") 197.94 + end 197.95 + open_head() 197.96 + slot.put( 197.97 + "EXECUTE ACTION: ", 197.98 + encode.html(node.module), 197.99 + "/", 197.100 + encode.html(node.action) 197.101 + ) 197.102 + close_head() 197.103 + if node.status == "softfail" then 197.104 + render_children( 197.105 + '<li class="trace_action_status">Status code: "' .. 197.106 + encode.html(node.failure_code) .. 197.107 + '"</li>' 197.108 + ) 197.109 + else 197.110 + render_children() 197.111 + end 197.112 + close() 197.113 + elseif node_type == "redirect" then 197.114 + open("redirect") 197.115 + open_head() 197.116 + slot.put("303 REDIRECT TO VIEW: ", encode.html(node.module), "/", encode.html(node.view)) 197.117 + close_with_children() 197.118 + elseif node_type == "forward" then 197.119 + open("forward") 197.120 + open_head() 197.121 + slot.put("INTERNAL FORWARD TO VIEW: ", encode.html(node.module), "/", encode.html(node.view)) 197.122 + close_with_children() 197.123 + elseif node_type == "exectime" then 197.124 + open("exectime") 197.125 + open_head() 197.126 + slot.put( 197.127 + "Finished after " .. 197.128 + string.format("%.1f", os.monotonic_hires_time() * 1000) .. 197.129 + ' ms (' .. 197.130 + string.format("%.1f", os.clock() * 1000) .. 197.131 + ' ms CPU)' 197.132 + ) 197.133 + close_with_children() 197.134 + elseif node_type == "sql" then 197.135 + open("sql") 197.136 + if node.error_position then 197.137 + -- error position starts counting with 1 197.138 + local part1 = string.sub(node.command, 1, node.error_position - 1) 197.139 + --local part2 = string.sub(node.command, node.error_position - 1, node.error_position + 1) 197.140 + local part2 = string.sub(node.command, node.error_position) 197.141 + slot.put(encode.html(part1)) 197.142 + slot.put('<span class="trace_error_position">⇒</span>') 197.143 + slot.put(encode.html(part2)) 197.144 + --slot.put('</span>') 197.145 + --slot.put(encode.html(part3)) 197.146 + else 197.147 + slot.put(encode.html(node.command)) 197.148 + end 197.149 + close(); 197.150 + elseif node_type == "error" then 197.151 + open("error") 197.152 + open_head() 197.153 + slot.put("UNEXPECTED ERROR") 197.154 + close_head() 197.155 + close() 197.156 + end 197.157 +end
198.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 198.2 +++ b/framework/env/trace/debug.lua Sun Oct 25 12:00:00 2009 +0100 198.3 @@ -0,0 +1,12 @@ 198.4 +--[[-- 198.5 +trace_debug( 198.6 + message -- message to be inserted into the trace log 198.7 +) 198.8 + 198.9 +This function can be used to include debug output in the trace log. 198.10 + 198.11 +--]]-- 198.12 + 198.13 +function trace.debug(message) 198.14 + trace._new_entry{ type = "debug", message = tostring(message) } 198.15 +end
199.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 199.2 +++ b/framework/env/trace/enter_action.lua Sun Oct 25 12:00:00 2009 +0100 199.3 @@ -0,0 +1,21 @@ 199.4 +--[[-- 199.5 +trace.enter_action{ 199.6 + module = module, 199.7 + action = action 199.8 +} 199.9 + 199.10 +This function is used by execute.action and logs the call of an action. 199.11 + 199.12 +--]]-- 199.13 + 199.14 +function trace.enter_action(args) 199.15 + local module = args.module 199.16 + local action = args.action 199.17 + if type(module) ~= "string" then 199.18 + error("No module string passed to trace.enter_action{...}.") 199.19 + end 199.20 + if type(action) ~= "string" then 199.21 + error("No action string passed to trace.enter_action{...}.") 199.22 + end 199.23 + trace._open_section{ type = "action", module = module, action = action } 199.24 +end
200.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 200.2 +++ b/framework/env/trace/enter_config.lua Sun Oct 25 12:00:00 2009 +0100 200.3 @@ -0,0 +1,16 @@ 200.4 +--[[-- 200.5 +trace.enter_config{ 200.6 + name = name 200.7 +} 200.8 + 200.9 +This function is used by execute.config and logs the inclusion of a configuration. 200.10 + 200.11 +--]]-- 200.12 + 200.13 +function trace.enter_config(args) 200.14 + local name = args.name 200.15 + if type(name) ~= "string" then 200.16 + error("No name string passed to trace.enter_config{...}.") 200.17 + end 200.18 + trace._open_section{ type = "config", name = name } 200.19 +end
201.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 201.2 +++ b/framework/env/trace/enter_filter.lua Sun Oct 25 12:00:00 2009 +0100 201.3 @@ -0,0 +1,16 @@ 201.4 +--[[-- 201.5 +trace.enter_filter{ 201.6 + path = path 201.7 +} 201.8 + 201.9 +This function logs the call of a filter, when using execute.filtered_view{...} and execute.filtered_action{...}. 201.10 + 201.11 +--]]-- 201.12 + 201.13 +function trace.enter_filter(args) 201.14 + local path = args.path 201.15 + if type(path) ~= "string" then 201.16 + error("No path string passed to trace.enter_filter{...}.") 201.17 + end 201.18 + trace._open_section{ type = "filter", path = path } 201.19 +end
202.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 202.2 +++ b/framework/env/trace/enter_view.lua Sun Oct 25 12:00:00 2009 +0100 202.3 @@ -0,0 +1,21 @@ 202.4 +--[[-- 202.5 +trace.enter_view{ 202.6 + module = module, 202.7 + view = view 202.8 +} 202.9 + 202.10 +This function is used by execute.view and logs the call of a view. 202.11 + 202.12 +--]]-- 202.13 + 202.14 +function trace.enter_view(args) 202.15 + local module = args.module 202.16 + local view = args.view 202.17 + if type(module) ~= "string" then 202.18 + error("No module passed to trace.enter_view{...}.") 202.19 + end 202.20 + if type(view) ~= "string" then 202.21 + error("No view passed to trace.enter_view{...}.") 202.22 + end 202.23 + trace._open_section{ type = "view", module = module, view = view } 202.24 +end
203.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 203.2 +++ b/framework/env/trace/error.lua Sun Oct 25 12:00:00 2009 +0100 203.3 @@ -0,0 +1,14 @@ 203.4 +--[[-- 203.5 +trace.error{ 203.6 +} 203.7 + 203.8 +This function is called automatically in case of errors to log them. 203.9 + 203.10 +--]]-- 203.11 + 203.12 +function trace.error(args) 203.13 + trace._new_entry { type = "error" } 203.14 + local closed_section = trace._close_section() 203.15 + closed_section.hard_error = true -- TODO: not used, maybe remove 203.16 + trace._stack = { trace._tree } 203.17 +end
204.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 204.2 +++ b/framework/env/trace/exectime.lua Sun Oct 25 12:00:00 2009 +0100 204.3 @@ -0,0 +1,21 @@ 204.4 +--[[-- 204.5 +trace.exectime{ 204.6 + real = real, -- physical time in seconds 204.7 + cpu = cpu -- CPU time in seconds 204.8 +} 204.9 + 204.10 +This function is called automatically to log the execution time of the handling of a request. 204.11 + 204.12 +--]]-- 204.13 + 204.14 +function trace.exectime(args) 204.15 + local real = args.real 204.16 + local cpu = args.cpu 204.17 + if type(real) ~= "number" then 204.18 + error("Called trace.exectime{...} without numeric 'real' argument.") 204.19 + end 204.20 + if type(cpu) ~= "number" then 204.21 + error("Called trace.exectime{...} without numeric 'cpu' argument.") 204.22 + end 204.23 + trace._new_entry{ type = "exectime", real = args.real, cpu = args.cpu } 204.24 +end
205.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 205.2 +++ b/framework/env/trace/execution_return.lua Sun Oct 25 12:00:00 2009 +0100 205.3 @@ -0,0 +1,20 @@ 205.4 +--[[-- 205.5 +trace.execution_return{ 205.6 + status = status -- optional status 205.7 +} 205.8 + 205.9 +This function is called automatically when returning from a view, action, configuration or filter for logging purposes. 205.10 + 205.11 +--]]-- 205.12 + 205.13 +function trace.execution_return(args) 205.14 + local status 205.15 + if args then 205.16 + status = args.status 205.17 + end 205.18 + if status and type(status) ~= "string" then 205.19 + error("Status passed to trace.execution_return{...} is not a string.") 205.20 + end 205.21 + local closed_section = trace._close_section() 205.22 + closed_section.status = status 205.23 +end
206.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 206.2 +++ b/framework/env/trace/forward.lua Sun Oct 25 12:00:00 2009 +0100 206.3 @@ -0,0 +1,21 @@ 206.4 +--[[-- 206.5 +trace.forward{ 206.6 + module = module, 206.7 + view = view 206.8 +} 206.9 + 206.10 +This function is called automatically by request.forward{...} for logging. 206.11 + 206.12 +--]]-- 206.13 + 206.14 +function trace.forward(args) 206.15 + local module = args.module 206.16 + local view = args.view 206.17 + if type(module) ~= "string" then 206.18 + error("No module string passed to trace.forward{...}.") 206.19 + end 206.20 + if type(view) ~= "string" then 206.21 + error("No view string passed to trace.forward{...}.") 206.22 + end 206.23 + trace._new_entry{ type = "forward", module = module, view = view } 206.24 +end
207.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 207.2 +++ b/framework/env/trace/redirect.lua Sun Oct 25 12:00:00 2009 +0100 207.3 @@ -0,0 +1,21 @@ 207.4 +--[[-- 207.5 +trace.redirect{ 207.6 + module = module, 207.7 + view = view 207.8 +} 207.9 + 207.10 +This function is called automatically by request.redirect{...} for logging. 207.11 + 207.12 +--]]-- 207.13 + 207.14 +function trace.redirect(args) 207.15 + local module = args.module 207.16 + local view = args.view 207.17 + if type(module) ~= "string" then 207.18 + error("No module string passed to trace.redirect{...}.") 207.19 + end 207.20 + if type(view) ~= "string" then 207.21 + error("No view string passed to trace.redirect{...}.") 207.22 + end 207.23 + trace._new_entry{ type = "redirect", module = module, view = view } 207.24 +end
208.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 208.2 +++ b/framework/env/trace/render.lua Sun Oct 25 12:00:00 2009 +0100 208.3 @@ -0,0 +1,11 @@ 208.4 +--[[-- 208.5 +trace.render() 208.6 + 208.7 +This function renders a trace log and writes it into the active slot. 208.8 + 208.9 +--]]-- 208.10 + 208.11 +function trace.render() 208.12 + -- TODO: check if all sections are closed? 208.13 + trace._render_sub_tree(trace._tree) 208.14 +end
209.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 209.2 +++ b/framework/env/trace/request.lua Sun Oct 25 12:00:00 2009 +0100 209.3 @@ -0,0 +1,37 @@ 209.4 +--[[-- 209.5 +trace.request{ 209.6 + module = module, 209.7 + view = view, 209.8 + action = action 209.9 +} 209.10 + 209.11 +This function is called automatically to log which view or action has been requested by the web browser. 209.12 + 209.13 +--]]-- 209.14 + 209.15 +function trace.request(args) 209.16 + local module = args.module 209.17 + local view = args.view 209.18 + local action = args.action 209.19 + if type(module) ~= "string" then 209.20 + error("No module string passed to trace.request{...}.") 209.21 + end 209.22 + if view and action then 209.23 + error("Both view and action passed to trace.request{...}.") 209.24 + end 209.25 + if not (view or action) then 209.26 + error("Neither view nor action passed to trace.request{...}.") 209.27 + end 209.28 + if view and type(view) ~= "string" then 209.29 + error("No view string passed to trace.request{...}.") 209.30 + end 209.31 + if action and type(action) ~= "string" then 209.32 + error("No action string passed to trace.request{...}.") 209.33 + end 209.34 + trace._new_entry{ 209.35 + type = "request", 209.36 + module = args.module, 209.37 + view = args.view, 209.38 + action = args.action 209.39 + } 209.40 +end
210.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 210.2 +++ b/framework/env/trace/restore_slots.lua Sun Oct 25 12:00:00 2009 +0100 210.3 @@ -0,0 +1,11 @@ 210.4 +--[[-- 210.5 +trace.restore_slots{ 210.6 +} 210.7 + 210.8 +This function is used to log the event of restoring previously stored slot contents. 210.9 + 210.10 +--]]-- 210.11 + 210.12 +function trace.restore_slots(args) 210.13 + trace._new_entry{ type = "restore_slots" } 210.14 +end
211.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 211.2 +++ b/framework/env/trace/sql.lua Sun Oct 25 12:00:00 2009 +0100 211.3 @@ -0,0 +1,27 @@ 211.4 +--[[-- 211.5 +trace.sql{ 211.6 + command = command, -- executed SQL command as string 211.7 + error_position = error_position -- optional position in bytes where an error occurred 211.8 +} 211.9 + 211.10 +This command can be used to log SQL command execution. It is currently not invoked automatically. 211.11 + 211.12 +--]]-- 211.13 + 211.14 +-- TODO: automatic use of this function? 211.15 + 211.16 +function trace.sql(args) 211.17 + local command = args.command 211.18 + local error_position = args.error_position 211.19 + if type(command) ~= "string" then 211.20 + error("No command string passed to trace.sql{...}.") 211.21 + end 211.22 + if error_position and type(error_position) ~= "number" then 211.23 + error("error_position must be a number.") 211.24 + end 211.25 + trace._new_entry{ 211.26 + type = "sql", 211.27 + command = command, 211.28 + error_position = error_position 211.29 + } 211.30 +end
212.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 212.2 +++ b/framework/env/ui/autofield.lua Sun Oct 25 12:00:00 2009 +0100 212.3 @@ -0,0 +1,59 @@ 212.4 +--[[-- 212.5 +ui.autofield{ 212.6 + name = name, -- field name (also used by default as HTML name) 212.7 + html_name = html_name, -- explicit HTML name to be used instead of 'name' 212.8 + value = nihil.lift(value), -- initial value, nil causes automatic lookup of value, use nihil.lift(nil) for nil 212.9 + container_attr = container_attr, -- extra HTML attributes for the container (div) enclosing field and label 212.10 + attr = attr, -- extra HTML attributes for the field 212.11 + label = label, -- text to be used as label for the input field 212.12 + label_attr = label_attr, -- extra HTML attributes for the label 212.13 + readonly = readonly_flag -- set to true, to force read-only mode 212.14 + record = record, -- record to be used, defaults to record given to ui.form{...} 212.15 + ... -- extra arguments for applicable ui.field.* helpers 212.16 +} 212.17 + 212.18 +This function automatically selects a ui.field.* helper to be used for a field of a record. 212.19 + 212.20 +--]]-- 212.21 + 212.22 +function ui.autofield(args) 212.23 + local args = table.new(args) 212.24 + assert(args.name, "ui.autofield{...} needs a field 'name'.") 212.25 + if not args.record then 212.26 + local slot_state = slot.get_state_table() 212.27 + if not slot_state then 212.28 + error("ui.autofield{...} was called without an explicit record to be used, and is also not called inside a form.") 212.29 + elseif not slot_state.form_record then 212.30 + error("ui.autofield{...} was called without an explicit record to be used, and the form does not have a record assigned either.") 212.31 + else 212.32 + args.record = slot_state.form_record 212.33 + end 212.34 + end 212.35 + local class = args.record._class 212.36 + assert(class, "Used ui.autofield{...} on a record with no class information stored in the '_class' attribute.") 212.37 + local fields, field_info, ui_field_type, ui_field_options 212.38 + fields = class.fields 212.39 + if fields then 212.40 + field_info = fields[args.name] 212.41 + end 212.42 + if field_info then 212.43 + ui_field_type = field_info.ui_field_type 212.44 + ui_field_options = table.new(field_info.ui_field_options) 212.45 + end 212.46 + if not ui_field_type then 212.47 + ui_field_type = "text" 212.48 + end 212.49 + if not ui_field_options then 212.50 + ui_field_options = {} 212.51 + end 212.52 + local ui_field_func = ui.field[ui_field_type] 212.53 + if not ui_field_func then 212.54 + error(string.format("Did not find ui.field helper of type %q.", ui_field_type)) 212.55 + end 212.56 + for key, value in pairs(ui_field_options) do 212.57 + if args[key] == nil then 212.58 + args[key] = value 212.59 + end 212.60 + end 212.61 + return ui_field_func(args) 212.62 +end
213.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 213.2 +++ b/framework/env/ui/container.lua Sun Oct 25 12:00:00 2009 +0100 213.3 @@ -0,0 +1,90 @@ 213.4 +--[[-- 213.5 +ui.container{ 213.6 + auto_args = auto_args, 213.7 + attr = attr, -- HTML attributes for the surrounding div or fieldset 213.8 + label = label, -- text to be used as label 213.9 + label_for = label_for, -- DOM id of element to which the label should refer 213.10 + label_attr = label_attr, -- extra HTML attributes for a label tag 213.11 + legend = legend, -- text to be used as legend 213.12 + legend_attr = legend_attr, -- HTML attributes for a legend tag 213.13 + content_first = content_first, -- set to true to place label or legend after the content 213.14 + content = function() 213.15 + ... -- 213.16 + end 213.17 +} 213.18 + 213.19 +This function encloses content in a div element (or a fieldset element, if 'legend' is given). An additional 'label' or 'legend' can be placed before the content or after the content. The argument 'auto_args' is set by other ui helper functions when calling ui.container automatically. 213.20 + 213.21 +--]]-- 213.22 + 213.23 +function ui.container(args) 213.24 + local attr, label, label_attr, legend, legend_attr, content 213.25 + local auto_args = args.auto_args 213.26 + if auto_args then 213.27 + attr = auto_args.container_attr 213.28 + label = auto_args.label 213.29 + label_attr = auto_args.label_attr 213.30 + legend = auto_args.legend 213.31 + legend_attr = auto_args.legend_attr 213.32 + if label and auto_args.attr and auto_args.attr.id then 213.33 + label_attr = table.new(label_attr) 213.34 + label_attr["for"] = auto_args.attr.id 213.35 + end 213.36 + else 213.37 + attr = args.attr 213.38 + label = args.label 213.39 + label_attr = args.label_attr or {} 213.40 + legend = args.legend 213.41 + legend_attr = args.legend_attr 213.42 + content = content 213.43 + if args.label_for then 213.44 + label_attr["for"] = args.label_for 213.45 + end 213.46 + end 213.47 + local content = args.content 213.48 + if label and not legend then 213.49 + return ui.tag { 213.50 + tag = "div", 213.51 + attr = attr, 213.52 + content = function() 213.53 + if not args.content_first then 213.54 + ui.tag{ tag = "label", attr = label_attr, content = label } 213.55 + slot.put(" ") 213.56 + end 213.57 + if type(content) == "function" then 213.58 + content() 213.59 + elseif content then 213.60 + slot.put(encode.html(content)) 213.61 + end 213.62 + if args.content_first then 213.63 + slot.put(" ") 213.64 + ui.tag{ tag = "label", attr = label_attr, content = label } 213.65 + end 213.66 + end 213.67 + } 213.68 + elseif legend and not label then 213.69 + return ui.tag { 213.70 + tag = "fieldset", 213.71 + attr = attr, 213.72 + content = function() 213.73 + if not args.content_first then 213.74 + ui.tag{ tag = "legend", attr = legend_attr, content = legend } 213.75 + slot.put(" ") 213.76 + end 213.77 + if type(content) == "function" then 213.78 + content() 213.79 + elseif content then 213.80 + slot.put(encode.html(content)) 213.81 + end 213.82 + if args.content_first then 213.83 + slot.put(" ") 213.84 + ui.tag{ tag = "legend", attr = legend_attr, content = legend } 213.85 + end 213.86 + end 213.87 + } 213.88 + elseif fieldset and label then 213.89 + error("ui.container{...} may either get a label or a legend.") 213.90 + else 213.91 + return ui.tag{ tag = "div", attr = attr, content = content } 213.92 + end 213.93 +end
214.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 214.2 +++ b/framework/env/ui/create_unique_id.lua Sun Oct 25 12:00:00 2009 +0100 214.3 @@ -0,0 +1,11 @@ 214.4 +--[[-- 214.5 +unique_id = -- unique string to be used as an id in the DOM tree 214.6 +ui.create_unique_id() 214.7 + 214.8 +This function returns a unique string to be used as an id in the DOM tree for elements. 214.9 + 214.10 +--]]-- 214.11 + 214.12 +function ui.create_unique_id() 214.13 + return "unique_" .. multirand.string(32, "bcdfghjklmnpqrstvwxyz") 214.14 +end
215.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 215.2 +++ b/framework/env/ui/field/boolean.lua Sun Oct 25 12:00:00 2009 +0100 215.3 @@ -0,0 +1,128 @@ 215.4 +--[[-- 215.5 +ui.field.boolean{ 215.6 + ... -- generic ui.field.* arguments, as described for ui.autofield{...} 215.7 + style = style, -- "radio" or "checkbox", 215.8 + nil_allowed = nil_allowed -- set to true, if nil is allowed as third value 215.9 +} 215.10 + 215.11 +This function inserts a field for boolean values in the active slot. For description of the generic field helper arguments, see help for ui.autofield{...}. 215.12 + 215.13 +--]]-- 215.14 + 215.15 +function ui.field.boolean(args) 215.16 + local style = args.style 215.17 + if not style then 215.18 + if args.nil_allowed then 215.19 + style = "radio" 215.20 + else 215.21 + style = "checkbox" 215.22 + end 215.23 + end 215.24 + local extra_args = { fetch_value = true } 215.25 + if not args.readonly and args.style == "radio" then 215.26 + extra_args.disable_label_for_id = true 215.27 + end 215.28 + ui.form_element(args, extra_args, function(args) 215.29 + local value = args.value 215.30 + if value ~= true and value ~= false and value ~= nil then 215.31 + error("Boolean value must be true, false or nil.") 215.32 + end 215.33 + if value == nil then 215.34 + if args.nil_allowed then 215.35 + value = args.default 215.36 + else 215.37 + value = args.default or false 215.38 + end 215.39 + end 215.40 + if args.readonly then 215.41 + ui.tag{ 215.42 + tag = args.tag, 215.43 + attr = args.attr, 215.44 + content = format.boolean(value, args.format_options) 215.45 + } 215.46 + elseif style == "radio" then 215.47 + local attr = table.new(args.attr) 215.48 + attr.type = "radio" 215.49 + attr.name = args.html_name 215.50 + attr.id = ui.create_unique_id() 215.51 + attr.value = "1" 215.52 + if value == true then 215.53 + attr.checked = "checked" 215.54 + else 215.55 + attr.checked = nil 215.56 + end 215.57 + ui.container{ 215.58 + attr = { class = "ui_radio_div" }, 215.59 + label = args.true_as or "Yes", -- TODO: localize 215.60 + label_for = attr.id, 215.61 + label_attr = { class = "ui_radio_label" }, 215.62 + content_first = true, 215.63 + content = function() 215.64 + ui.tag{ tag = "input", attr = attr } 215.65 + end 215.66 + } 215.67 + attr.id = ui.create_unique_id() 215.68 + attr.value = "0" 215.69 + if value == false then 215.70 + attr.checked = "1" 215.71 + else 215.72 + attr.checked = nil 215.73 + end 215.74 + ui.container{ 215.75 + attr = { class = "ui_radio_div" }, 215.76 + label = args.false_as or "No", -- TODO: localize 215.77 + label_for = attr.id, 215.78 + label_attr = { class = "ui_radio_label" }, 215.79 + content_first = true, 215.80 + content = function() 215.81 + ui.tag{ tag = "input", attr = attr } 215.82 + end 215.83 + } 215.84 + if args.nil_allowed then 215.85 + attr.id = ui.create_unique_id() 215.86 + attr.value = "" 215.87 + if value == nil then 215.88 + attr.checked = "1" 215.89 + else 215.90 + attr.checked = nil 215.91 + end 215.92 + ui.container{ 215.93 + attr = { class = "ui_radio_div" }, 215.94 + label = args.nil_as or "N/A", -- TODO: localize 215.95 + label_for = attr.id, 215.96 + label_attr = { class = "ui_radio_label" }, 215.97 + content_first = true, 215.98 + content = function() 215.99 + ui.tag{ tag = "input", attr = attr } 215.100 + end 215.101 + } 215.102 + end 215.103 + ui.hidden_field{ 215.104 + name = args.html_name .. "__format", value = "boolean" 215.105 + } 215.106 + elseif style == "checkbox" then 215.107 + if args.nil_allowed then 215.108 + error("Checkboxes do not support nil values.") 215.109 + end 215.110 + local attr = table.new(args.attr) 215.111 + attr.type = "checkbox" 215.112 + attr.name = args.html_name 215.113 + attr.value = "1" 215.114 + if value then 215.115 + attr.checked = "checked" 215.116 + else 215.117 + attr.checked = nil 215.118 + end 215.119 + ui.tag{ tag = "input", attr = attr } 215.120 + ui.hidden_field{ 215.121 + name = args.html_name .. "__format", 215.122 + value = encode.format_info( 215.123 + "boolean", 215.124 + { true_as = "1", false_as = "" } 215.125 + ) 215.126 + } 215.127 + else 215.128 + error("'style' attribute for ui.field.boolean{...} must be set to \"radio\", \"checkbox\" or nil.") 215.129 + end 215.130 + end) 215.131 +end
216.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 216.2 +++ b/framework/env/ui/field/date.lua Sun Oct 25 12:00:00 2009 +0100 216.3 @@ -0,0 +1,84 @@ 216.4 +--[[-- 216.5 +ui.field.date{ 216.6 + ... -- generic ui.field.* arguments, as described for ui.autofield{...} 216.7 +} 216.8 + 216.9 +This function inserts a field for dates in the active slot. If the JavaScript library "gregor.js" has been loaded, a rich input field is used. For description of the generic field helper arguments, see help for ui.autofield{...}. 216.10 + 216.11 +--]]-- 216.12 + 216.13 +function ui.field.date(args) 216.14 + ui.form_element(args, {fetch_value = true}, function(args) 216.15 + local value_string = format.date(args.value, args.format_options) 216.16 + if args.readonly then 216.17 + ui.tag{ tag = args.tag, attr = args.attr, content = value_string } 216.18 + else 216.19 + local fallback_data = slot.use_temporary(function() 216.20 + local attr = table.new(args.attr) 216.21 + attr.type = "text" 216.22 + attr.name = args.html_name 216.23 + attr.value = value_string 216.24 + attr.class = attr.class or "ui_field_date" 216.25 + ui.tag{ tag = "input", attr = attr } 216.26 + ui.hidden_field{ 216.27 + name = args.html_name .. "__format", 216.28 + value = encode.format_info("date", args.format_options) 216.29 + } 216.30 + end) 216.31 + local user_field_id, hidden_field_id 216.32 + local helper_data = slot.use_temporary(function() 216.33 + local attr = table.new(args.attr) 216.34 + user_field_id = attr.id or ui.create_unique_id() 216.35 + hidden_field_id = ui.create_unique_id() 216.36 + attr.id = user_field_id 216.37 + attr.type = "text" 216.38 + attr.class = attr.class or "ui_field_date" 216.39 + ui.tag{ tag = "input", attr = attr } 216.40 + local attr = table.new(args.attr) 216.41 + attr.id = hidden_field_id 216.42 + attr.type = "hidden" 216.43 + attr.name = args.html_name 216.44 + attr.value = atom.dump(args.value) -- extra safety for JS failure 216.45 + ui.tag{ 216.46 + tag = "input", 216.47 + attr = { 216.48 + id = hidden_field_id, 216.49 + type = "hidden", 216.50 + name = args.html_name 216.51 + } 216.52 + } 216.53 + end) 216.54 + -- TODO: localization 216.55 + ui.script{ 216.56 + noscript = fallback_data, 216.57 + type = "text/javascript", 216.58 + content = function() 216.59 + slot.put( 216.60 + "if (gregor_addGui == null) document.write(", 216.61 + encode.json(fallback_data), 216.62 + "); else { document.write(", 216.63 + encode.json(helper_data), 216.64 + "); gregor_addGui({element_id: ", 216.65 + encode.json(user_field_id), 216.66 + ", month_names: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'], weekday_names: ['Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa', 'Su'], week_numbers: 'left', format: 'DD.MM.YYYY', selected: " 216.67 + ) 216.68 + if (args.value) then 216.69 + slot.put( 216.70 + "{year: ", tostring(args.value.year), 216.71 + ", month: ", tostring(args.value.month), 216.72 + ", day: ", tostring(args.value.day), 216.73 + "}" 216.74 + ) 216.75 + else 216.76 + slot.put("null") 216.77 + end 216.78 + slot.put( 216.79 + ", select_callback: function(date) { document.getElementById(", 216.80 + encode.json(hidden_field_id), 216.81 + ").value = (date == null) ? '' : date.iso_string; } } ) }" 216.82 + ) 216.83 + end 216.84 + } 216.85 + end 216.86 + end) 216.87 +end
217.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 217.2 +++ b/framework/env/ui/field/hidden.lua Sun Oct 25 12:00:00 2009 +0100 217.3 @@ -0,0 +1,20 @@ 217.4 +--[[-- 217.5 +ui.field.hidden{ 217.6 + ... -- generic ui.field.* arguments, as described for ui.autofield{...} 217.7 +} 217.8 + 217.9 +This function inserts a hidden form field in the active slot. It is a high level function compared to ui.hidden_field{...}. If called inside a read-only form, then this function does nothing. 217.10 + 217.11 +--]]-- 217.12 + 217.13 +function ui.field.hidden(args) 217.14 + ui.form_element(args, {fetch_value = true}, function(args) 217.15 + if not args.readonly then 217.16 + ui.hidden_field{ 217.17 + attr = args.attr, 217.18 + name = args.html_name, 217.19 + value = args.value 217.20 + } 217.21 + end 217.22 + end) 217.23 +end
218.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 218.2 +++ b/framework/env/ui/field/integer.lua Sun Oct 25 12:00:00 2009 +0100 218.3 @@ -0,0 +1,28 @@ 218.4 +--[[-- 218.5 +ui.field.integer{ 218.6 + ... -- generic ui.field.* arguments, as described for ui.autofield{...} 218.7 + format_options = format_options -- format options for format.decimal 218.8 +} 218.9 + 218.10 +This function inserts a field for an integer in the active slot. For description of the generic field helper arguments, see help for ui.autofield{...}. 218.11 + 218.12 +--]]-- 218.13 + 218.14 +function ui.field.integer(args) 218.15 + ui.form_element(args, {fetch_value = true}, function(args) 218.16 + local value_string = format.decimal(args.value, args.format_options) 218.17 + if args.readonly then 218.18 + ui.tag{ tag = args.tag, attr = args.attr, content = value_string } 218.19 + else 218.20 + local attr = table.new(args.attr) 218.21 + attr.type = "text" 218.22 + attr.name = args.html_name 218.23 + attr.value = value_string 218.24 + ui.tag{ tag = "input", attr = attr } 218.25 + ui.hidden_field{ 218.26 + name = args.html_name .. "__format", 218.27 + value = encode.format_info("decimal", args.format_options) 218.28 + } 218.29 + end 218.30 + end) 218.31 +end
219.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 219.2 +++ b/framework/env/ui/field/password.lua Sun Oct 25 12:00:00 2009 +0100 219.3 @@ -0,0 +1,23 @@ 219.4 +--[[-- 219.5 +ui.field.password{ 219.6 + ... -- generic ui.field.* arguments, as described for ui.autofield{...} 219.7 +} 219.8 + 219.9 +This function inserts a field for a password in the active slot. For read-only forms this function does nothing. For description of the generic field helper arguments, see help for ui.autofield{...}. 219.10 + 219.11 +--]]-- 219.12 + 219.13 +function ui.field.password(args) 219.14 + ui.form_element(args, {fetch_value = true}, function(args) 219.15 + local value_string = atom.dump(args.value) 219.16 + if args.readonly then 219.17 + -- nothing 219.18 + else 219.19 + local attr = table.new(args.attr) 219.20 + attr.type = "password" 219.21 + attr.name = args.html_name 219.22 + attr.value = value_string 219.23 + ui.tag{ tag = "input", attr = attr } 219.24 + end 219.25 + end) 219.26 +end
220.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 220.2 +++ b/framework/env/ui/field/select.lua Sun Oct 25 12:00:00 2009 +0100 220.3 @@ -0,0 +1,68 @@ 220.4 +--[[-- 220.5 +ui.field.select{ 220.6 + ... -- generic ui.field.* arguments, as described for ui.autofield{...} 220.7 + foreign_records = foreign_records, -- list of records to be chosen from, or function returning such a list 220.8 + foreign_id = foreign_id, -- name of id field in foreign records 220.9 + foreign_name = foreign_name, -- name of field to be used as name in foreign records 220.10 + format_options = format_options -- format options for format.string 220.11 +} 220.12 + 220.13 +This function inserts a select field in the active slot. For description of the generic field helper arguments, see help for ui.autofield{...}. 220.14 + 220.15 +--]]-- 220.16 + 220.17 +function ui.field.select(args) 220.18 + ui.form_element(args, {fetch_value = true}, function(args) 220.19 + local foreign_records = args.foreign_records 220.20 + if type(foreign_records) == "function" then 220.21 + foreign_records = foreign_records(args.record) 220.22 + end 220.23 + if args.readonly then 220.24 + local name 220.25 + for idx, record in ipairs(foreign_records) do 220.26 + if record[args.foreign_id] == args.value then 220.27 + name = record[args.foreign_name] 220.28 + break 220.29 + end 220.30 + end 220.31 + ui.tag{ 220.32 + tag = args.tag, 220.33 + attr = args.attr, 220.34 + content = format.string(name, args.format_options) 220.35 + } 220.36 + else 220.37 + local attr = table.new(args.attr) 220.38 + attr.name = args.html_name 220.39 + ui.tag{ 220.40 + tag = "select", 220.41 + attr = attr, 220.42 + content = function() 220.43 + if args.nil_as then 220.44 + ui.tag{ 220.45 + tag = "option", 220.46 + attr = { value = "" }, 220.47 + content = format.string( 220.48 + args.nil_as, 220.49 + args.format_options 220.50 + ) 220.51 + } 220.52 + end 220.53 + for idx, record in ipairs(foreign_records) do 220.54 + local key = record[args.foreign_id] 220.55 + ui.tag{ 220.56 + tag = "option", 220.57 + attr = { 220.58 + value = key, 220.59 + selected = ((key == args.value) and "selected" or nil) 220.60 + }, 220.61 + content = format.string( 220.62 + record[args.foreign_name], 220.63 + args.format_options 220.64 + ) 220.65 + } 220.66 + end 220.67 + end 220.68 + } 220.69 + end 220.70 + end) 220.71 +end
221.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 221.2 +++ b/framework/env/ui/field/text.lua Sun Oct 25 12:00:00 2009 +0100 221.3 @@ -0,0 +1,28 @@ 221.4 +--[[-- 221.5 +ui.field.text{ 221.6 + ... -- generic ui.field.* arguments, as described for ui.autofield{...} 221.7 + format_options = format_options -- format options for format.string 221.8 +} 221.9 + 221.10 +This function inserts a field for a text in the active slot. For description of the generic field helper arguments, see help for ui.autofield{...}. 221.11 + 221.12 +--]]-- 221.13 + 221.14 +function ui.field.text(args) 221.15 + ui.form_element(args, {fetch_value = true}, function(args) 221.16 + local value_string = format.string(args.value, args.format_options) 221.17 + if args.readonly then 221.18 + ui.tag{ tag = args.tag, attr = args.attr, content = value_string } 221.19 + else 221.20 + local attr = table.new(args.attr) 221.21 + attr.name = args.html_name 221.22 + if args.multiline then 221.23 + ui.tag { tag = "textarea", attr = attr, content = value_string } 221.24 + else 221.25 + attr.type = "text" 221.26 + attr.value = value_string 221.27 + ui.tag{ tag = "input", attr = attr } 221.28 + end 221.29 + end 221.30 + end) 221.31 +end
222.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 222.2 +++ b/framework/env/ui/form.lua Sun Oct 25 12:00:00 2009 +0100 222.3 @@ -0,0 +1,99 @@ 222.4 +--[[-- 222.5 +ui.form{ 222.6 + record = record, -- optional record to be used 222.7 + read_only = read_only, -- set to true, if form should be read-only (no submit button) 222.8 + external = external, -- external URL to be used as HTML form action 222.9 + module = module, -- module name to be used for HTML form action 222.10 + view = view, -- view name to be used for HTML form action 222.11 + action = action, -- action name to be used for HTML form action 222.12 + routing = { 222.13 + default = { -- default routing for called action 222.14 + mode = mode, -- "forward" or "redirect" 222.15 + module = module, -- optional module name, defaults to current module 222.16 + view = view, -- view name 222.17 + id = id, -- optional id to be passed to the view 222.18 + params = params -- optional params to be passed to the view 222.19 + }, 222.20 + ok = { ... }, -- routing when "ok" is returned by the called action 222.21 + error = { ... }, -- routing when "error" is returned by the called action 222.22 + ... = { ... } -- routing when "..." is returned by the called action 222.23 + } 222.24 + content = function() 222.25 + ... -- code creating the contents of the form 222.26 + end 222.27 +} 222.28 + 222.29 +This functions creates a web form, which encloses the content created by the given 'content' function. When a 'record' is given, ui.field.* helper functions will be able to automatically determine field values by using the given record. If 'read_only' is set to true, then a call of ui.submit{...} will be ignored, and ui.field.* helper functions will behave differently. 222.30 + 222.31 +--]]-- 222.32 + 222.33 +function ui.form(args) 222.34 + local args = args or {} 222.35 + local slot_state = slot.get_state_table() 222.36 + local old_record = slot_state.form_record 222.37 + local old_readonly = slot_state.form_readonly 222.38 + slot_state.form_record = args.record 222.39 + if args.readonly then 222.40 + slot_state.form_readonly = true 222.41 + ui.container{ attr = args.attr, content = args.content } 222.42 + else 222.43 + slot_state.form_readonly = false 222.44 + local params = table.new(args.params) 222.45 + local routing_default_given = false 222.46 + if args.routing then 222.47 + for status, settings in pairs(args.routing) do 222.48 + if status == "default" then 222.49 + routing_default_given = true 222.50 + end 222.51 + local module = settings.module or args.module or request.get_module() 222.52 + assert(settings.mode, "No mode specified in routing entry.") 222.53 + assert(settings.view, "No view specified in routing entry.") 222.54 + params["_webmcp_routing." .. status .. ".mode"] = settings.mode 222.55 + params["_webmcp_routing." .. status .. ".module"] = module 222.56 + params["_webmcp_routing." .. status .. ".view"] = settings.view 222.57 + params["_webmcp_routing." .. status .. ".id"] = settings.id 222.58 + if settings.params then 222.59 + for key, value in pairs(settings.params) do 222.60 + params["_webmcp_routing." .. status .. ".params." .. key] = value 222.61 + end 222.62 + end 222.63 + end 222.64 + end 222.65 + if not routing_default_given then 222.66 + params["_webmcp_routing.default.mode"] = "forward" 222.67 + params["_webmcp_routing.default.module"] = request.get_module() 222.68 + params["_webmcp_routing.default.view"] = request.get_view() 222.69 + end 222.70 + params._webmcp_csrf_secret = request.get_csrf_secret() 222.71 + local attr = table.new(args.attr) 222.72 + attr.action = encode.url{ 222.73 + external = args.external, 222.74 + module = args.module or request.get_module(), 222.75 + view = args.view, 222.76 + action = args.action, 222.77 + } 222.78 + attr.method = args.method and string.upper(args.method) or "POST" 222.79 + if slot_state.form_opened then 222.80 + error("Cannot open a non-readonly form inside a non-readonly form.") 222.81 + end 222.82 + slot_state.form_opened = true 222.83 + ui.tag { 222.84 + tag = "form", 222.85 + attr = attr, 222.86 + content = function() 222.87 + if args.id then 222.88 + ui.hidden_field{ name = "_webmcp_id", value = args.id } 222.89 + end 222.90 + for key, value in pairs(params) do 222.91 + ui.hidden_field{ name = key, value = value } 222.92 + end 222.93 + if args.content then 222.94 + args.content() 222.95 + end 222.96 + end 222.97 + } 222.98 + slot_state.form_opened = false 222.99 + end 222.100 + slot_state.form_readonly = old_readonly 222.101 + slot_state.form_record = old_record 222.102 +end
223.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 223.2 +++ b/framework/env/ui/form_element.lua Sun Oct 25 12:00:00 2009 +0100 223.3 @@ -0,0 +1,81 @@ 223.4 +--[[-- 223.5 +ui.form_element( 223.6 + args, -- external arguments 223.7 + { -- options for this function call 223.8 + fetch_value = fetch_value_flag, -- true causes automatic determination of args.value, if nil 223.9 + fetch_record = fetch_record_flag, -- true causes automatic determination of args.record, if nil 223.10 + disable_label_for_id = disable_label_for_id_flag, -- true suppresses automatic setting of args.attr.id for a HTML label_for reference 223.11 + }, 223.12 + function(args) 223.13 + ... -- program code 223.14 + end 223.15 +) 223.16 + 223.17 +This function helps other form helpers by preprocessing arguments passed to the helper, e.g. fetching a value from a record stored in a state-table of the currently active slot. 223.18 + 223.19 +--]]-- 223.20 + 223.21 +-- TODO: better documentation 223.22 + 223.23 +function ui.form_element(args, extra_args, func) 223.24 + local args = table.new(args) 223.25 + if extra_args then 223.26 + for key, value in pairs(extra_args) do 223.27 + args[key] = value 223.28 + end 223.29 + end 223.30 + local slot_state = slot.get_state_table() 223.31 + args.html_name = args.html_name or args.name 223.32 + if args.fetch_value then 223.33 + if args.value == nil then 223.34 + if not args.record and slot_state then 223.35 + args.record = slot_state.form_record 223.36 + end 223.37 + if args.record then 223.38 + args.value = args.record[args.name] 223.39 + end 223.40 + else 223.41 + args.value = nihil.lower(args.value) 223.42 + end 223.43 + elseif args.fetch_record then 223.44 + if not args.record and slot_state then 223.45 + args.record = slot_state.form_record 223.46 + end 223.47 + end 223.48 + if 223.49 + args.html_name and 223.50 + not args.readonly and 223.51 + slot_state.form_readonly == false 223.52 + then 223.53 + args.readonly = false 223.54 + local prefix 223.55 + if args.html_name_prefix == nil then 223.56 + prefix = slot_state.html_name_prefix 223.57 + else 223.58 + prefix = args.html_name_prefix 223.59 + end 223.60 + if prefix then 223.61 + args.html_name = prefix .. args.html_name 223.62 + end 223.63 + else 223.64 + args.readonly = true 223.65 + end 223.66 + if args.label then 223.67 + if not args.disable_label_for_id then 223.68 + if not args.attr then 223.69 + args.attr = { id = ui.create_unique_id() } 223.70 + elseif not args.attr.id then 223.71 + args.attr.id = ui.create_unique_id() 223.72 + end 223.73 + end 223.74 + if not args.label_attr then 223.75 + args.label_attr = { class = "ui_field_label" } 223.76 + elseif not args.label_attr.class then 223.77 + args.label_attr.class = "ui_field_label" 223.78 + end 223.79 + end 223.80 + ui.container{ 223.81 + auto_args = args, 223.82 + content = function() return func(args) end 223.83 + } 223.84 +end
224.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 224.2 +++ b/framework/env/ui/heading.lua Sun Oct 25 12:00:00 2009 +0100 224.3 @@ -0,0 +1,18 @@ 224.4 +--[[-- 224.5 +ui.heading{ 224.6 + level = level, -- level from 1 to 6, defaults to 1 224.7 + attr = attr, -- extra HTML attributes 224.8 + content = content -- string or function for content 224.9 +} 224.10 + 224.11 +This function inserts a heading into the active slot. 224.12 + 224.13 +--]]-- 224.14 + 224.15 +function ui.heading(args) 224.16 + return ui.tag{ 224.17 + tag = "h" .. (args.level or 1), 224.18 + attr = args.attr, 224.19 + content = args.content 224.20 + } 224.21 +end
225.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 225.2 +++ b/framework/env/ui/hidden_field.lua Sun Oct 25 12:00:00 2009 +0100 225.3 @@ -0,0 +1,19 @@ 225.4 +--[[-- 225.5 +ui.hidden_field{ 225.6 + name = name, -- HTML name 225.7 + value = value, -- value 225.8 + attr = attr -- extra HTML attributes 225.9 +} 225.10 + 225.11 +This function inserts a hidden form field in the active slot. It is a low level function compared to ui.field.hidden{...}. 225.12 + 225.13 +--]]-- 225.14 + 225.15 +function ui.hidden_field(args) 225.16 + local args = args or {} 225.17 + local attr = table.new(args.attr) 225.18 + attr.type = "hidden" 225.19 + attr.name = args.name 225.20 + attr.value = atom.dump(args.value) 225.21 + return ui.tag{ tag = "input", attr = attr } 225.22 +end
226.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 226.2 +++ b/framework/env/ui/image.lua Sun Oct 25 12:00:00 2009 +0100 226.3 @@ -0,0 +1,13 @@ 226.4 +function ui.image(args) 226.5 + local args = args or {} 226.6 + local attr = table.new(args.attr) 226.7 + attr.src = encode.url{ 226.8 + external = args.external, 226.9 + static = args.static, 226.10 + module = args.module or request.get_module(), 226.11 + view = args.view, 226.12 + id = args.id, 226.13 + params = args.params, 226.14 + } 226.15 + return ui.tag{ tag = "img", attr = attr } 226.16 +end
227.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 227.2 +++ b/framework/env/ui/link.lua Sun Oct 25 12:00:00 2009 +0100 227.3 @@ -0,0 +1,90 @@ 227.4 +--[[-- 227.5 +ui.link{ 227.6 + external = external, -- external URL 227.7 + static = static, -- URL relative to the static file directory 227.8 + module = module, -- module name 227.9 + view = view, -- view name 227.10 + action = action, -- action name 227.11 + id = id, -- optional id to be passed to the view or action to select a particular data record 227.12 + params = params, -- optional parameters to be passed to the view or action 227.13 + routing = routing, -- optional routing information for action links, as described for ui.form{...} 227.14 + text = text, -- link text 227.15 + content = content -- alternative name for 'text' option, preferred for functions 227.16 +} 227.17 + 227.18 +This function inserts a link into the active slot. It may be either an internal application link ('module' given and 'view' or 'action' given), or a link to an external web page ('external' given), or a link to a file in the static file directory of the application ('static' given). 227.19 + 227.20 +--]]-- 227.21 + 227.22 +function ui.link(args) 227.23 + local args = args or {} 227.24 + local content = args.text or args.content -- TODO: decide which argument name to use 227.25 + assert(content, "ui.link{...} needs a text.") 227.26 + local function wrapped_content() 227.27 + -- TODO: icon/image 227.28 + if type(content) == "function" then 227.29 + content() 227.30 + else 227.31 + slot.put(encode.html(content)) 227.32 + end 227.33 + end 227.34 + if args.action then 227.35 + local form_attr = table.new(args.form_attr) 227.36 + local form_id 227.37 + if form_attr.id then 227.38 + form_id = form_attr.id 227.39 + else 227.40 + form_id = ui.create_unique_id() 227.41 + end 227.42 + local quoted_form_id = encode.json(form_id) 227.43 + form_attr.id = form_id 227.44 + local a_attr = table.new(args.attr) 227.45 + a_attr.href = "#" 227.46 + a_attr.onclick = 227.47 + "var f = document.getElementById(" .. quoted_form_id .. "); if (! f.onsubmit || f.onsubmit() != false) { f.submit() };" 227.48 + ui.form{ 227.49 + external = args.external, 227.50 + module = args.module or request.get_module(), 227.51 + action = args.action, 227.52 + id = args.id, 227.53 + params = args.params, 227.54 + routing = args.routing, 227.55 + attr = form_attr, 227.56 + content = function() 227.57 + ui.submit{ text = args.text, attr = args.submit_attr } 227.58 + end 227.59 + } 227.60 + ui.script{ 227.61 + type = "text/javascript", 227.62 + script = ( 227.63 + "document.getElementById(" .. 227.64 + quoted_form_id .. 227.65 + ").style.display = 'none'; document.write(" .. 227.66 + encode.json( 227.67 + slot.use_temporary( 227.68 + function() 227.69 + ui.tag{ 227.70 + tag = "a", 227.71 + attr = a_attr, 227.72 + content = wrapped_content 227.73 + } 227.74 + end 227.75 + ) 227.76 + ) .. 227.77 + ");" 227.78 + ) 227.79 + } 227.80 + else 227.81 + -- TODO: support content function 227.82 + local a_attr = table.new(args.attr) 227.83 + a_attr.href = encode.url{ 227.84 + external = args.external, 227.85 + static = args.static, 227.86 + module = args.module or request.get_module(), 227.87 + view = args.view, 227.88 + id = args.id, 227.89 + params = args.params, 227.90 + } 227.91 + return ui.tag{ tag = "a", attr = a_attr, content = wrapped_content } 227.92 + end 227.93 +end
228.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 228.2 +++ b/framework/env/ui/list.lua Sun Oct 25 12:00:00 2009 +0100 228.3 @@ -0,0 +1,227 @@ 228.4 +--[[-- 228.5 +ui.list{ 228.6 + label = list_label, -- optional label for the whole list 228.7 + style = style, -- "table", "ulli" or "div" 228.8 + prefix = prefix, -- prefix for HTML field names 228.9 + records = records, -- array of records to be displayed as rows in the list 228.10 + columns = { 228.11 + { 228.12 + label = column_label, -- label for the column 228.13 + label_attr = label_attr, -- table with HTML attributes for the heading cell or div 228.14 + field_attr = field_attr, -- table with HTML attributes for the data cell or div 228.15 + name = name, -- name of the field in each record 228.16 + html_name = html_name, -- optional html-name for writable fields (defaults to name) 228.17 + ui_field_type = ui_field_type, -- name of the ui.field.* function to use 228.18 + ...., -- other options for the given ui.field.* functions 228.19 + format = format, -- name of the format function to be used (if not using ui_field_type) 228.20 + format_options = format_options, -- options to be passed to the format function 228.21 + content = content -- function to output field data per record (ignoring name, format, ...) 228.22 + }, 228.23 + { ... }, 228.24 + ... 228.25 + } 228.26 +} 228.27 + 228.28 +This function takes an array of records to be displayed in a list. The whole list may have a label. The style's "table" (for <table>), "ulli" (for <ul><li>) and "div" (just using <div> tags) are supported. For each column several options must be specified. 228.29 + 228.30 +--]]-- 228.31 + 228.32 +-- TODO: documentation of the prefix option 228.33 +-- TODO: check short descriptions of fields in documentation 228.34 +-- TODO: field_attr is used for the OUTER html tag's attributes, while attr is used for the INNER html tag's attributes (produced by ui.field.*), is that okay? 228.35 +-- TODO: use field information of record class, if no columns are given 228.36 +-- TODO: callback to set row attr's for a specific row 228.37 + 228.38 +function ui.list(args) 228.39 + local args = args or {} 228.40 + local label = args.label 228.41 + local list_type = args.style or "table" 228.42 + local prefix = args.prefix 228.43 + local records = assert(args.records, "ui.list{...} needs records.") 228.44 + local columns = assert(args.columns, "ui.list{...} needs column definitions.") 228.45 + local outer_attr = table.new(args.attr) 228.46 + local header_existent = false 228.47 + for idx, column in ipairs(columns) do 228.48 + if column.label then 228.49 + header_existent = true 228.50 + break 228.51 + end 228.52 + end 228.53 + local slot_state = slot.get_state_table() 228.54 + local outer_tag, head_tag, head_tag2, label_tag, body_tag, row_tag 228.55 + if list_type == "table" then 228.56 + outer_tag = "table" 228.57 + head_tag = "thead" 228.58 + head_tag2 = "tr" 228.59 + label_tag = "th" 228.60 + body_tag = "tbody" 228.61 + row_tag = "tr" 228.62 + field_tag = "td" 228.63 + elseif list_type == "ulli" then 228.64 + outer_tag = "div" 228.65 + head_tag = "div" 228.66 + label_tag = "div" 228.67 + body_tag = "ul" 228.68 + row_tag = "li" 228.69 + field_tag = "td" 228.70 + elseif list_type == "div" then 228.71 + outer_tag = "div" 228.72 + head_tag = "div" 228.73 + label_tag = "div" 228.74 + body_tag = "div" 228.75 + row_tag = "div" 228.76 + field_tag = "div" 228.77 + else 228.78 + error("Unknown list type specified for ui.list{...}.") 228.79 + end 228.80 + outer_attr.class = outer_attr.class or "ui_list" 228.81 + ui.container{ 228.82 + auto_args = args, 228.83 + content = function() 228.84 + ui.tag{ 228.85 + tag = outer_tag, 228.86 + attr = outer_attr, 228.87 + content = function() 228.88 + if header_existent then 228.89 + ui.tag{ 228.90 + tag = head_tag, 228.91 + attr = { class = "ui_list_head" }, 228.92 + content = function() 228.93 + local function header_content() 228.94 + for idx, column in ipairs(columns) do 228.95 + if column.ui_field_type ~= "hidden" then 228.96 + local label_attr = table.new(column.label_attr) 228.97 + label_attr.class = 228.98 + label_attr.class or { class = "ui_list_label" } 228.99 + ui.tag{ 228.100 + tag = label_tag, 228.101 + attr = label_attr, 228.102 + content = column.label or "" 228.103 + } 228.104 + end 228.105 + end 228.106 + end 228.107 + if head_tag2 then 228.108 + ui.tag{ tag = head_tag2, content = header_content } 228.109 + else 228.110 + header_content() 228.111 + end 228.112 + end 228.113 + } 228.114 + end 228.115 + ui.tag{ 228.116 + tag = body_tag, 228.117 + attr = { class = "ui_list_body" }, 228.118 + content = function() 228.119 + for record_idx, record in ipairs(records) do 228.120 + local row_class 228.121 + if record_idx % 2 == 0 then 228.122 + row_class = "ui_list_row ui_list_even" 228.123 + else 228.124 + row_class = "ui_list_row ui_list_odd" 228.125 + end 228.126 + ui.tag{ 228.127 + tag = row_tag, 228.128 + attr = { class = row_class }, 228.129 + content = function() 228.130 + local old_html_name_prefix, old_form_record 228.131 + if prefix then 228.132 + old_html_name_prefix = slot_state.html_name_prefix 228.133 + old_form_record = slot_state.form_record 228.134 + slot_state.html_name_prefix = prefix .. "[" .. record_idx .. "]" 228.135 + slot_state.form_record = record 228.136 + end 228.137 + local first_column = true 228.138 + for column_idx, column in ipairs(columns) do 228.139 + if column.ui_field_type ~= "hidden" then 228.140 + local field_attr = table.new(column.field_attr) 228.141 + field_attr.class = 228.142 + field_attr.class or { class = "ui_list_field" } 228.143 + local field_content 228.144 + if column.content then 228.145 + field_content = function() 228.146 + return column.content(record) 228.147 + end 228.148 + elseif column.name then 228.149 + if column.ui_field_type then 228.150 + local ui_field_func = ui.field[column.ui_field_type] 228.151 + if not ui_field_func then 228.152 + error('Unknown ui_field_type "' .. column.ui_field_type .. '".') 228.153 + end 228.154 + local ui_field_options = table.new(column) 228.155 + ui_field_options.record = record 228.156 + ui_field_options.label = nil 228.157 + if not prefix and ui_field_options.readonly == nil then 228.158 + ui_field_options.readonly = true 228.159 + end 228.160 + field_content = function() 228.161 + return ui.field[column.ui_field_type](ui_field_options) 228.162 + end 228.163 + elseif column.format then 228.164 + local formatter = format[column.format] 228.165 + if not formatter then 228.166 + error('Unknown format "' .. column.format .. '".') 228.167 + end 228.168 + field_content = formatter( 228.169 + record[column.name], column.format_options 228.170 + ) 228.171 + else 228.172 + field_content = function() 228.173 + return ui.autofield{ 228.174 + record = record, 228.175 + name = column.name, 228.176 + html_name = column.html_name 228.177 + } 228.178 + end 228.179 + end 228.180 + else 228.181 + error("Each column needs either a 'content' or a 'name'.") 228.182 + end 228.183 + local extended_field_content 228.184 + if first_column then 228.185 + first_column = false 228.186 + extended_field_content = function() 228.187 + for column_idx, column in ipairs(columns) do 228.188 + if column.ui_field_type == "hidden" then 228.189 + local ui_field_options = table.new(column) 228.190 + ui_field_options.record = record 228.191 + ui_field_options.label = nil 228.192 + if not prefix and ui_field_options.readonly == nil then 228.193 + ui_field_options.readonly = true 228.194 + end 228.195 + ui.field.hidden(ui_field_options) 228.196 + end 228.197 + end 228.198 + field_content() 228.199 + end 228.200 + else 228.201 + extended_field_content = field_content 228.202 + end 228.203 + ui.tag{ 228.204 + tag = field_tag, 228.205 + attr = field_attr, 228.206 + content = extended_field_content 228.207 + } 228.208 + end 228.209 + end 228.210 + if prefix then 228.211 + slot_state.html_name_prefix = old_html_name_prefix 228.212 + slot_state.form_record = old_form_record 228.213 + end 228.214 + end 228.215 + } 228.216 + end 228.217 + end 228.218 + } 228.219 + end 228.220 + } 228.221 + end 228.222 + } 228.223 + if prefix then 228.224 + -- ui.field.hidden is used instead of ui.hidden_field to suppress output in case of read-only mode. 228.225 + ui.field.hidden{ 228.226 + html_name = prefix .. "[len]", 228.227 + value = #records 228.228 + } 228.229 + end 228.230 +end
229.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 229.2 +++ b/framework/env/ui/multiselect.lua Sun Oct 25 12:00:00 2009 +0100 229.3 @@ -0,0 +1,138 @@ 229.4 +--[[-- 229.5 +ui.multiselect{ 229.6 + name = name, -- HTML name ('html_name' is NOT a valid argument for this function) 229.7 + container_attr = container_attr, -- extra HTML attributes for the container (div) enclosing field and label 229.8 + attr = attr, -- extra HTML attributes for the field 229.9 + label = label, -- text to be used as label for the input field 229.10 + label_attr = label_attr, -- extra HTML attributes for the label 229.11 + readonly = readonly_flag -- set to true, to force read-only mode 229.12 + foreign_records = foreign_records, -- list of records to be chosen from, or function returning such a list 229.13 + foreign_id = foreign_id, -- name of id field in foreign records 229.14 + foreign_name = foreign_name, -- name of field to be used as name in foreign records 229.15 + selected_ids = selected_ids, -- list of ids of currently selected foreign records 229.16 + connecting_records = connecting_records, -- list of connection entries, determining which foreign records are currently selected 229.17 + own_id = own_id, -- TODO documentation needed 229.18 + own_reference = own_reference, -- name of foreign key field in connecting records, which references the main record 229.19 + foreign_reference = foreign_reference, -- name of foreign key field in connecting records, which references foreign records 229.20 + format_options = format_options -- format options for format.string 229.21 +} 229.22 + 229.23 +This function inserts a select field with possibility of multiple selections in the active slot. This function does not reside within ui.field.*, because multiple selections are not stored within a field of a record, but within a different SQL table. Note that 'html_name' is NOT a valid argument to this function. For description of the generic field helper arguments, see help for ui.autofield{...}. 229.24 + 229.25 +--]]-- 229.26 + 229.27 +function ui.multiselect(args) 229.28 + local style = args.style or "checkbox" 229.29 + local extra_args = { fetch_record = true } 229.30 + if not args.readonly and args.style == "checkbox" then 229.31 + extra_args.disable_label_for_id = true 229.32 + end 229.33 + ui.form_element(args, extra_args, function(args) 229.34 + local foreign_records = args.foreign_records 229.35 + if type(foreign_records) == "function" then 229.36 + foreign_records = foreign_records(args.record) 229.37 + end 229.38 + local connecting_records = args.connecting_records 229.39 + if type(connecting_records) == "function" then 229.40 + connecting_records = connecting_records(args.record) 229.41 + end 229.42 + local select_hash = {} 229.43 + if args.selected_ids then 229.44 + for idx, selected_id in ipairs(args.selected_ids) do 229.45 + select_hash[selected_id] = true 229.46 + end 229.47 + elseif args.own_reference then 229.48 + for idx, connecting_record in ipairs(args.connecting_records) do 229.49 + if connecting_record[args.own_reference] == args.record[args.own_id] then 229.50 + select_hash[connecting_record[args.foreign_reference]] = true 229.51 + end 229.52 + end 229.53 + else 229.54 + for idx, connecting_record in ipairs(args.connecting_records) do 229.55 + select_hash[connecting_record[args.foreign_reference]] = true 229.56 + end 229.57 + end 229.58 + local attr = table.new(args.attr) 229.59 + if not attr.class then 229.60 + attr.class = "ui_multi_selection" 229.61 + end 229.62 + if args.readonly then 229.63 + ui.tag{ 229.64 + tag = "ul", 229.65 + attr = attr, 229.66 + content = function() 229.67 + for idx, record in ipairs(foreign_records) do 229.68 + if select_hash[record[args.foreign_id]] then 229.69 + ui.tag{ 229.70 + tag = "li", 229.71 + content = format.string( 229.72 + record[args.foreign_name], 229.73 + args.format_options 229.74 + ) 229.75 + } 229.76 + end 229.77 + end 229.78 + end 229.79 + } 229.80 + elseif style == "select" then 229.81 + attr.name = args.name 229.82 + attr.multiple = "multiple" 229.83 + ui.tag{ 229.84 + tag = "select", 229.85 + attr = attr, 229.86 + content = function() 229.87 + if args.nil_as then 229.88 + ui.tag{ 229.89 + tag = "option", 229.90 + attr = { value = "" }, 229.91 + content = format.string( 229.92 + args.nil_as, 229.93 + args.format_options 229.94 + ) 229.95 + } 229.96 + end 229.97 + for idx, record in ipairs(foreign_records) do 229.98 + local key = record[args.foreign_id] 229.99 + local selected = select_hash[key] 229.100 + ui.tag{ 229.101 + tag = "option", 229.102 + attr = { 229.103 + value = key, 229.104 + selected = (selected and "selected" or nil) 229.105 + }, 229.106 + content = format.string( 229.107 + record[args.foreign_name], 229.108 + args.format_options 229.109 + ) 229.110 + } 229.111 + end 229.112 + end 229.113 + } 229.114 + elseif style == "checkbox" then 229.115 + attr.type = "checkbox" 229.116 + attr.name = args.name 229.117 + for idx, record in ipairs(foreign_records) do 229.118 + local key = record[args.foreign_id] 229.119 + local selected = select_hash[key] 229.120 + attr.id = ui.create_unique_id() 229.121 + attr.value = key 229.122 + attr.checked = selected and "checked" or nil 229.123 + ui.container{ 229.124 + label = format.string( 229.125 + record[args.foreign_name], 229.126 + args.format_options 229.127 + ), 229.128 + attr = { class = "ui_checkbox_div" }, 229.129 + label_for = attr.id, 229.130 + label_attr = { class = "ui_checkbox_label" }, 229.131 + content_first = true, 229.132 + content = function() 229.133 + ui.tag{ tag = "input", attr = attr } 229.134 + end 229.135 + } 229.136 + end 229.137 + else 229.138 + error("'style' attribute for ui.multiselect{...} must be set to \"select\", \"checkbox\" or nil.") 229.139 + end 229.140 + end) 229.141 +end
230.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 230.2 +++ b/framework/env/ui/paginate.lua Sun Oct 25 12:00:00 2009 +0100 230.3 @@ -0,0 +1,69 @@ 230.4 +--[[-- 230.5 +ui.paginate{ 230.6 + selector = selector, -- a selector for items from the database 230.7 + per_page = per_page, -- items per page, defaults to 10 230.8 + name = name, -- name of the CGI get variable, defaults to "page" 230.9 + content = function() 230.10 + ... -- code block which should be encapsulated with page selection links 230.11 + end 230.12 +} 230.13 + 230.14 +This function preceeds and appends the output of the given 'content' function with page selection links. The passed selector will be modified to show only a limited amount ('per_page') of items. 230.15 +--]]-- 230.16 + 230.17 +function ui.paginate(args) 230.18 + local selector = args.selector 230.19 + local per_page = args.per_page or 10 230.20 + local name = args.name or 'page' 230.21 + local content = args.content 230.22 + local count_selector = selector:get_db_conn():new_selector() 230.23 + count_selector:add_field('count(1)') 230.24 + count_selector:add_from(selector) 230.25 + count_selector:single_object_mode() 230.26 + local count = count_selector:exec().count 230.27 + local page_count = math.floor((count - 1) / per_page) + 1 230.28 + local current_page = param.get(name, atom.integer) or 1 230.29 + selector:limit(per_page) 230.30 + selector:offset((current_page - 1) * per_page) 230.31 + local id = param.get_id_cgi() 230.32 + local params = param.get_all_cgi() 230.33 + local function pagination_elements() 230.34 + if page_count > 1 then 230.35 + for page = 1, page_count do 230.36 + if page > 1 then 230.37 + slot.put(" ") 230.38 + end 230.39 + params[name] = page 230.40 + local attr = {} 230.41 + if current_page == page then 230.42 + attr.class = "active" 230.43 + end 230.44 + ui.link{ 230.45 + attr = attr, 230.46 + module = request.get_module(), 230.47 + view = request.get_view(), 230.48 + id = id, 230.49 + params = params, 230.50 + text = tostring(page) 230.51 + } 230.52 + end 230.53 + end 230.54 + end 230.55 + ui.container{ 230.56 + attr = { class = 'ui_paginate' }, 230.57 + content = function() 230.58 + ui.container{ 230.59 + attr = { class = 'ui_paginate_head ui_paginate_select' }, 230.60 + content = pagination_elements 230.61 + } 230.62 + ui.container{ 230.63 + attr = { class = 'ui_paginate_content' }, 230.64 + content = content 230.65 + } 230.66 + ui.container{ 230.67 + attr = { class = 'ui_paginate_foot ui_paginate_select' }, 230.68 + content = pagination_elements 230.69 + } 230.70 + end 230.71 + } 230.72 +end
231.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 231.2 +++ b/framework/env/ui/script.lua Sun Oct 25 12:00:00 2009 +0100 231.3 @@ -0,0 +1,35 @@ 231.4 +--[[-- 231.5 +ui.script{ 231.6 + noscript_attr = noscript_attr, -- HTML attributes for noscript tag 231.7 + noscript = noscript, -- string or function for noscript content 231.8 + attr = attr, -- extra HTML attributes for script tag 231.9 + type = type, -- type of script, defaults to "text/javascript" 231.10 + script = script, -- string or function for script content 231.11 +} 231.12 + 231.13 +This function is used to insert a script into the active slot. It is currently not XML compliant and the script must not contain a closing script tag. 231.14 + 231.15 +--]]-- 231.16 + 231.17 +-- TODO: CDATA or SGML comment? 231.18 + 231.19 +function ui.script(args) 231.20 + local args = args or {} 231.21 + local noscript_attr = args.noscript_attr 231.22 + local noscript = args.noscript 231.23 + local attr = table.new(args.attr) 231.24 + attr.type = attr.type or args.type or "text/javascript" 231.25 + local script = args.script 231.26 + if script and type(script) ~= "function" then 231.27 + -- disable HTML entity escaping 231.28 + script = function() 231.29 + slot.put(args.script) 231.30 + end 231.31 + end 231.32 + if noscript then 231.33 + ui.tag{ tag = "noscript", attr = attr, content = noscript } 231.34 + end 231.35 + if script then 231.36 + ui.tag{ tag = "script", attr = attr, content = script } 231.37 + end 231.38 +end
232.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 232.2 +++ b/framework/env/ui/submit.lua Sun Oct 25 12:00:00 2009 +0100 232.3 @@ -0,0 +1,21 @@ 232.4 +--[[-- 232.5 +ui.submit{ 232.6 + name = name, -- optional HTML name 232.7 + value = value, -- HTML value 232.8 + text = value -- text on button 232.9 +} 232.10 + 232.11 +This function places a HTML form submit button in the active slot. Currently the text displayed on the button and the value attribute are the same, so specifying both a 'value' and a 'text' makes no sense. 232.12 + 232.13 +--]]-- 232.14 + 232.15 +function ui.submit(args) 232.16 + if slot.get_state_table().form_readonly == false then 232.17 + local args = args or {} 232.18 + local attr = table.new(attr) 232.19 + attr.type = "submit" 232.20 + attr.name = args.name 232.21 + attr.value = args.value or args.text 232.22 + return ui.tag{ tag = "input", attr = attr } 232.23 + end 232.24 +end
233.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 233.2 +++ b/framework/env/ui/tag.lua Sun Oct 25 12:00:00 2009 +0100 233.3 @@ -0,0 +1,50 @@ 233.4 +--[[-- 233.5 +ui.tag{ 233.6 + tag = tag, -- HTML tag, e.g. "a" for <a>...</a> 233.7 + attr = attr, -- table of HTML attributes, e.g. { class = "hide" } 233.8 + content = content -- string to be HTML encoded, or function to be executed 233.9 +} 233.10 + 233.11 +This function writes a HTML tag into the active slot. 233.12 + 233.13 +NOTE: ACCELERATED FUNCTION 233.14 +Do not change unless also you also update webmcp_accelerator.c 233.15 + 233.16 +--]]-- 233.17 + 233.18 +function ui.tag(args) 233.19 + local tag, attr, content 233.20 + tag = args.tag 233.21 + attr = args.attr or {} 233.22 + content = args.content 233.23 + if type(attr.class) == "table" then 233.24 + attr = table.new(attr) 233.25 + attr.class = table.concat(attr.class, " ") 233.26 + end 233.27 + if not tag and next(attr) then 233.28 + tag = "span" 233.29 + end 233.30 + if tag then 233.31 + slot.put('<', tag) 233.32 + for key, value in pairs(attr) do 233.33 + slot.put(' ', key, '="', encode.html(value), '"') 233.34 + end 233.35 + end 233.36 + if content then 233.37 + if tag then 233.38 + slot.put('>') 233.39 + end 233.40 + if type(content) == "function" then 233.41 + content() 233.42 + else 233.43 + slot.put(encode.html(content)) 233.44 + end 233.45 + if tag then 233.46 + slot.put('</', tag, '>') 233.47 + end 233.48 + else 233.49 + if tag then 233.50 + slot.put(' />') 233.51 + end 233.52 + end 233.53 +end
234.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 234.2 +++ b/framework/env/ui_deprecated/__init.lua Sun Oct 25 12:00:00 2009 +0100 234.3 @@ -0,0 +1,2 @@ 234.4 +ui_deprecated._form = {} 234.5 +
235.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 235.2 +++ b/framework/env/ui_deprecated/_prepare_redirect_params.lua Sun Oct 25 12:00:00 2009 +0100 235.3 @@ -0,0 +1,26 @@ 235.4 +function ui_deprecated._prepare_redirect_params(params, redirect_to) 235.5 + if redirect_to then 235.6 + for status, settings in pairs(redirect_to) do 235.7 + local module, view = settings.module, settings.view 235.8 + if not module then 235.9 + error("No redirection module specified.") 235.10 + end 235.11 + if not view then 235.12 + error("No redirection view specified.") 235.13 + end 235.14 + if status == "ok" then 235.15 + params["_webmcp_routing." .. status .. ".mode"] = "redirect" 235.16 + else 235.17 + params["_webmcp_routing." .. status .. ".mode"] = "forward" 235.18 + end 235.19 + params["_webmcp_routing." .. status .. ".module"] = settings.module 235.20 + params["_webmcp_routing." .. status .. ".view"] = settings.view 235.21 + params["_webmcp_routing." .. status .. ".id"] = settings.id 235.22 + if settings.params then 235.23 + for key, value in pairs(settings.params) do 235.24 + params["_webmcp_routing." .. status .. ".params." .. key] = value 235.25 + end 235.26 + end 235.27 + end 235.28 + end 235.29 +end
236.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 236.2 +++ b/framework/env/ui_deprecated/_stringify_table.lua Sun Oct 25 12:00:00 2009 +0100 236.3 @@ -0,0 +1,11 @@ 236.4 +function ui_deprecated._stringify_table(table) 236.5 + if not table then 236.6 + return '' 236.7 + end 236.8 + local string = '' 236.9 + for key, value in pairs(table) do 236.10 + string = string .. ' ' .. key .. '="' .. value ..'"' 236.11 + end 236.12 + return string 236.13 +end 236.14 +
237.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 237.2 +++ b/framework/env/ui_deprecated/box.lua Sun Oct 25 12:00:00 2009 +0100 237.3 @@ -0,0 +1,29 @@ 237.4 +--[[doc: ui_deprecated.box 237.5 + 237.6 +Starts a box 237.7 + 237.8 +label (string) Label, optional 237.9 +class (string) Style class, optional 237.10 +content (function) or (string) The content of the box 237.11 + 237.12 + 237.13 +Example: 237.14 + 237.15 +ui_deprecated.box{ 237.16 + label = 'My box label', 237.17 + class = 'my_css_class', 237.18 + content = function() 237.19 + ui_deprecated.text('My text') 237.20 + end 237.21 +} 237.22 + 237.23 +--]] 237.24 + 237.25 +function ui_deprecated.box(args) 237.26 + if args.class then 237.27 + args.html_options = args.html_options or {} 237.28 + args.html_options.class = args.class 237.29 + end 237.30 + ui_deprecated.tag('div', args) 237.31 +end 237.32 +
238.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 238.2 +++ b/framework/env/ui_deprecated/calendar.lua Sun Oct 25 12:00:00 2009 +0100 238.3 @@ -0,0 +1,147 @@ 238.4 +function ui_deprecated.calendar(args) 238.5 + local record = assert(slot.get_state_table(), "ui_deprecated.calender was not called within a form.").form_record 238.6 + local value = param.get(args.field, atom.date) or record[args.field] 238.7 + local year = param.get('_ui_calendar_year', atom.integer) or args.year or 2008 238.8 + local month = param.get('_ui_calendar_month', atom.integer) or args.month or 10 238.9 + local empty_days = atom.date{ year = year, month = month, day = 1 }.iso_weekday -1 238.10 + local enabled = not args.disabled 238.11 + 238.12 + local prev_year = year 238.13 + local prev_month = month - 1 238.14 + if prev_month == 0 then 238.15 + prev_month = 12 238.16 + prev_year = prev_year - 1 238.17 + end 238.18 + 238.19 + local next_year = year 238.20 + local next_month = month + 1 238.21 + if next_month == 13 then 238.22 + next_month = 1 238.23 + next_year = next_year + 1 238.24 + end 238.25 + 238.26 + ui_deprecated.tag('div', { 238.27 + html_options = { 238.28 + class="ui_field ui_calendar" 238.29 + }, 238.30 + content = function() 238.31 + ui_deprecated.tag('input', { 238.32 + html_options = { 238.33 + type = 'hidden', 238.34 + value = year, 238.35 + name = '_ui_calendar_year', 238.36 + id = '_ui_calendar_year', 238.37 + onchange = 'this.form.submit();' 238.38 + } 238.39 + }) 238.40 + ui_deprecated.tag('input', { 238.41 + html_options = { 238.42 + type = 'hidden', 238.43 + value = month, 238.44 + name = '_ui_calendar_month', 238.45 + id = '_ui_calendar_month', 238.46 + onchange = 'this.form.submit();' 238.47 + } 238.48 + }) 238.49 + ui_deprecated.tag('input', { 238.50 + html_options = { 238.51 + type = 'hidden', 238.52 + value = value and tostring(value) or '', 238.53 + name = args.field, 238.54 + id = '_ui_calendar_input', 238.55 + onchange = 'this.form.submit();' 238.56 + } 238.57 + }) 238.58 + if args.label then 238.59 + ui_deprecated.tag('div', { 238.60 + html_options = { 238.61 + class="label" 238.62 + }, 238.63 + content = function() 238.64 + ui_deprecated.text(args.label) 238.65 + end 238.66 + }) 238.67 + end 238.68 + ui_deprecated.tag('div', { 238.69 + html_options = { 238.70 + class="value" 238.71 + }, 238.72 + content = function() 238.73 + ui_deprecated.tag('div', { 238.74 + html_options = { 238.75 + class = 'next', 238.76 + href = '#', 238.77 + onclick = enabled and "document.getElementById('_ui_calendar_year').value = '" .. tostring(next_year) .. "'; document.getElementById('_ui_calendar_month').value = '" .. tostring(next_month) .. "'; document.getElementById('_ui_calendar_year').form.submit();" or '', 238.78 + }, 238.79 + content = '>>>'; 238.80 + }) 238.81 + ui_deprecated.tag('div', { 238.82 + html_options = { 238.83 + class = 'prev', 238.84 + href = '#', 238.85 + onclick = enabled and "document.getElementById('_ui_calendar_year').value = '" .. tostring(prev_year) .. "'; document.getElementById('_ui_calendar_month').value = '" .. tostring(prev_month) .. "'; document.getElementById('_ui_calendar_year').form.submit();" or '', 238.86 + }, 238.87 + content = '<<<'; 238.88 + }) 238.89 + ui_deprecated.tag('div', { 238.90 + html_options = { 238.91 + class="title" 238.92 + }, 238.93 + content = function() 238.94 + local months = {_'January', _'February', _'March', _'April', _'May', _'June', _'July', _'August', _'September', _'October', _'November', _'December' } 238.95 + ui_deprecated.text(months[month]) 238.96 + ui_deprecated.text(' ') 238.97 + ui_deprecated.text(tostring(year)) 238.98 + end 238.99 + }) 238.100 + ui_deprecated.tag('table', { 238.101 + content = function() 238.102 + ui_deprecated.tag('thead', { 238.103 + content = function() 238.104 + ui_deprecated.tag('tr', { 238.105 + content = function() 238.106 + local dows = { _'Mon', _'Tue', _'Wed', _'Thu', _'Fri', _'Sat', _'Sun' } 238.107 + for col = 1,7 do 238.108 + ui_deprecated.tag('th', { content = dows[col] }) 238.109 + end 238.110 + end 238.111 + }) 238.112 + end 238.113 + }) 238.114 + ui_deprecated.tag('tbody', { 238.115 + content = function() 238.116 + for row = 1,6 do 238.117 + ui_deprecated.tag('tr', { 238.118 + content = function() 238.119 + for col = 1,7 do 238.120 + local day = (row -1) * 7 + col - empty_days 238.121 + local date = atom.date.invalid 238.122 + if day > 0 then 238.123 + date = atom.date{ year = year, month = month, day = day } 238.124 + end 238.125 + ui_deprecated.tag('td', { 238.126 + html_options = { 238.127 + onclick = enabled and 'document.getElementById(\'_ui_calendar_input\').value = \'' .. tostring(date) .. '\'; document.getElementById(\'_ui_calendar_input\').onchange(); ' or '' 238.128 + }, 238.129 + content = function() 238.130 + if date.invalid then 238.131 + slot.put(' ') 238.132 + else 238.133 + local selected = date == value 238.134 + args.day_func(date, selected) 238.135 + end 238.136 + end 238.137 + }) 238.138 + end 238.139 + end 238.140 + }) 238.141 + end 238.142 + end 238.143 + }) 238.144 + end 238.145 + }) 238.146 + end 238.147 + }) 238.148 + end 238.149 + }) 238.150 +end
239.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 239.2 +++ b/framework/env/ui_deprecated/field.lua Sun Oct 25 12:00:00 2009 +0100 239.3 @@ -0,0 +1,34 @@ 239.4 +-- 239.5 +-- Creates an output field 239.6 +-- 239.7 +-- label (string) The label of the field 239.8 +-- value (atom) The value to put out 239.9 +-- field_type (string) The type of the field (default: 'string') 239.10 +-- 239.11 +-- Example: 239.12 +-- 239.13 +-- ui_deprecated.field({ 239.14 +-- label = _'Id', 239.15 +-- value = myobject.id, 239.16 +-- field_type = 'integer' 239.17 +-- }) 239.18 +-- 239.19 + 239.20 +function ui_deprecated.field(args) 239.21 + local value_type = args.value_type or atom.string 239.22 + slot.put( 239.23 + '<div class="ui_field ui_field_', value_type.name, '">', 239.24 + '<div class="label">', 239.25 + encode.html(args.label or ''), 239.26 + '</div>', 239.27 + '<div class="value">') 239.28 + if args.value then 239.29 + slot.put(encode.html(convert.to_human(args.value, value_type))) 239.30 + elseif args.link then 239.31 + ui_deprecated.link(args.link) 239.32 + end 239.33 + slot.put( 239.34 + '</div>', 239.35 + '</div>\n' 239.36 + ) 239.37 +end
240.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 240.2 +++ b/framework/env/ui_deprecated/form.lua Sun Oct 25 12:00:00 2009 +0100 240.3 @@ -0,0 +1,85 @@ 240.4 +-- 240.5 +-- Creates a formular 240.6 +-- 240.7 +-- record (record) Take field values from this object (optional) 240.8 +-- class (string) Style class (optional) 240.9 +-- method (string) Submit method ['post', 'get'] (optional) 240.10 +-- module (string) The module to submit to 240.11 +-- action (string) The action to submit to 240.12 +-- params (table) The GET parameter to be send with 240.13 +-- content (function) Function for the content of the form 240.14 +-- 240.15 +-- Example: 240.16 +-- 240.17 +-- ui_deprecated.form({ 240.18 +-- object = client, 240.19 +-- class = 'form_class', 240.20 +-- method = 'post', 240.21 +-- module = 'client', 240.22 +-- action = 'update', 240.23 +-- params = { 240.24 +-- id = client.id 240.25 +-- }, 240.26 +-- redirect_to = { 240.27 +-- ok = { 240.28 +-- module = 'client', 240.29 +-- view = 'list' 240.30 +-- }, 240.31 +-- error = { 240.32 +-- module = 'client', 240.33 +-- view = 'edit', 240.34 +-- params = { 240.35 +-- id = client.id 240.36 +-- } 240.37 +-- } 240.38 +-- }, 240.39 +-- }) 240.40 + 240.41 +function ui_deprecated.form(args) 240.42 + local slot_state = slot.get_state_table() 240.43 + if slot_state.form_opened then 240.44 + error("Cannot open a form inside a form.") 240.45 + end 240.46 + slot_state.form_opened = true 240.47 + local old_record = slot_state.form_record 240.48 + slot_state.form_record = args.record or {} -- TODO: decide what really should happen when no record is specified 240.49 + 240.50 + local params = {} 240.51 + if args.params then 240.52 + for key, value in pairs(args.params) do 240.53 + params[key] = value 240.54 + end 240.55 + end 240.56 + ui_deprecated._prepare_redirect_params(params, args.redirect_to) 240.57 + 240.58 + local attr_action = args.url or encode.url{ 240.59 + module = args.module, 240.60 + view = args.view, 240.61 + action = args.action, 240.62 + id = args.id, 240.63 + params = params 240.64 + } 240.65 + 240.66 + local base = request.get_relative_baseurl() 240.67 + local attr_class = table.concat({ 'ui_form', args.class }, ' ') 240.68 + local attr_method = args.method or 'POST' -- TODO: uppercase/sanatize method 240.69 + 240.70 + slot.put( 240.71 + '<form', 240.72 + ' action="', attr_action, '"', 240.73 + ' class="', attr_class, '"', 240.74 + ' method="', attr_method, '"', 240.75 + '>\n' 240.76 + ) 240.77 + 240.78 + if type(args.content) == 'function' then 240.79 + args.content() 240.80 + else 240.81 + error('No content function') 240.82 + end 240.83 + 240.84 + slot.put('</form>') 240.85 + slot_state.form_record = old_record 240.86 + slot_state.form_opened = false 240.87 + 240.88 +end
241.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 241.2 +++ b/framework/env/ui_deprecated/image.lua Sun Oct 25 12:00:00 2009 +0100 241.3 @@ -0,0 +1,22 @@ 241.4 +-- 241.5 +-- Creates an image 241.6 +-- 241.7 +-- image (string) 241.8 +-- 241.9 +-- Example: 241.10 +-- 241.11 +-- ui_deprecated.image({ 241.12 +-- image = 'test.png', 241.13 +-- }) 241.14 +-- 241.15 + 241.16 +function ui_deprecated.image(args) 241.17 + assert(args.image, "No image argument given.") 241.18 + slot.put( 241.19 + '<img src="', 241.20 + request.get_relative_baseurl(), 241.21 + 'static/', 241.22 + args.image, 241.23 + '" />' 241.24 + ) 241.25 +end
242.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 242.2 +++ b/framework/env/ui_deprecated/input.lua Sun Oct 25 12:00:00 2009 +0100 242.3 @@ -0,0 +1,67 @@ 242.4 +-- 242.5 +-- Creates an input field in a form 242.6 +-- 242.7 +-- label (string) The label of the input field 242.8 +-- field (string) The name of the record field 242.9 +-- field_type (string) The type of the record field 242.10 +-- 242.11 +-- Example: 242.12 +-- 242.13 +-- ui_deprecated.input({ 242.14 +-- label = _'Comment', 242.15 +-- field = 'comment', 242.16 +-- field_type = 'textarea' 242.17 +-- }) 242.18 +-- 242.19 +local field_type_to_atom_class_map = { 242.20 + text = atom.string, 242.21 + textarea = atom.string, 242.22 + number = atom.number, 242.23 + percentage = atom.number, 242.24 +} 242.25 + 242.26 +function ui_deprecated.input(args) 242.27 + local record = assert(slot.get_state_table(), "ui_deprecated.input was not called within a form.").form_record 242.28 + 242.29 + local field_type = args.field_type or "text" 242.30 + 242.31 + local field_func = assert(ui_deprecated.input_field[field_type], "no field helper for given type '" .. field_type .. "'") 242.32 + 242.33 + local html_name = args.name or args.field 242.34 + local field_html 242.35 + 242.36 + if args.field then 242.37 + local param_type = field_type_to_atom_class_map[field_type] or error('Unkown field type') 242.38 + field_html = field_func{ 242.39 + name = html_name, 242.40 + value = param.get(html_name, param_type) 242.41 + or record[args.field], 242.42 + height = args.height, 242.43 + } 242.44 + 242.45 + elseif args.value then 242.46 + field_html = field_func{ 242.47 + name = html_name, 242.48 + value = args.value, 242.49 + height = args.height, 242.50 + } 242.51 + 242.52 + else 242.53 + field_html = field_func{ 242.54 + name = html_name, 242.55 + value = '', 242.56 + height = args.height, 242.57 + } 242.58 + 242.59 + end 242.60 + 242.61 + slot.put('<div class="ui_field ui_input_', field_type, '">\n') 242.62 + if args.label then 242.63 + slot.put('<div class="label">', encode.html(args.label), '</div>\n') 242.64 + end 242.65 + slot.put('<div class="value">', 242.66 + field_html, 242.67 + '</div>\n', 242.68 + '</div>\n' 242.69 + ) 242.70 +end
246.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 246.2 +++ b/framework/env/ui_deprecated/input_field/number.lua Sun Oct 25 12:00:00 2009 +0100 246.3 @@ -0,0 +1,5 @@ 246.4 +function ui_deprecated.input_field.number(args) 246.5 + name = args.name or '' 246.6 + value = args.value or 0 246.7 + return '<input class="ui_input_field_number" type="text" name="' .. name .. '" value="' .. convert.to_human(value, "number") .. '" />' 246.8 +end 246.9 \ No newline at end of file
247.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 247.2 +++ b/framework/env/ui_deprecated/input_field/percentage.lua Sun Oct 25 12:00:00 2009 +0100 247.3 @@ -0,0 +1,5 @@ 247.4 +function ui_deprecated.input_field.percentage(args) 247.5 + name = args.name or '' 247.6 + value = args.value or 0 247.7 + return '<input class="ui_input_field_percentage" type="text" name="' .. name .. '" value="' .. convert.to_human(value, "number") .. '" /> %' 247.8 +end 247.9 \ No newline at end of file
249.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 249.2 +++ b/framework/env/ui_deprecated/input_field/text.lua Sun Oct 25 12:00:00 2009 +0100 249.3 @@ -0,0 +1,5 @@ 249.4 +function ui_deprecated.input_field.text(args) 249.5 + name = args.name or '' 249.6 + value = args.value or '' 249.7 + return '<input type="text" name="' .. name .. '" value="' .. convert.to_human(value, "string") .. '" />' 249.8 +end 249.9 \ No newline at end of file
250.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 250.2 +++ b/framework/env/ui_deprecated/input_field/textarea.lua Sun Oct 25 12:00:00 2009 +0100 250.3 @@ -0,0 +1,9 @@ 250.4 +function ui_deprecated.input_field.textarea(args) 250.5 + local value = args.value or '' 250.6 + local name = assert(args.name, 'No field name given') 250.7 + local style = '' 250.8 + if args.height then 250.9 + style = 'style="height:' .. args.height .. '"' 250.10 + end 250.11 + return '<textarea name="' .. name .. '" ' .. style .. '>' .. encode.html(convert.to_human(value, "string")) .. '</textarea>' 250.12 +end
252.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 252.2 +++ b/framework/env/ui_deprecated/label.lua Sun Oct 25 12:00:00 2009 +0100 252.3 @@ -0,0 +1,5 @@ 252.4 +function ui_deprecated.label(value) 252.5 + slot.put '<div class="ui_label">' 252.6 + ui_deprecated.text( value ) 252.7 + slot.put '</div>\n' 252.8 +end 252.9 \ No newline at end of file
253.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 253.2 +++ b/framework/env/ui_deprecated/link.lua Sun Oct 25 12:00:00 2009 +0100 253.3 @@ -0,0 +1,69 @@ 253.4 +function ui_deprecated.link(args) 253.5 + if args.action then 253.6 + local params = {} 253.7 + if args.params then 253.8 + for key, value in pairs(args.params) do 253.9 + params[key] = value 253.10 + end 253.11 + end 253.12 + ui_deprecated._prepare_redirect_params(params, args.redirect_to) 253.13 + 253.14 + local attr_action = args.url or encode.url{ 253.15 + module = args.module or request.get_module(), 253.16 + action = args.action, 253.17 + id = args.id, 253.18 + params = params 253.19 + } 253.20 + local attr_class = table.concat({ 'ui_link', args.class }, ' ') 253.21 + local attr_target = args.target or '' 253.22 + local redirect_to = args.redirect_to 253.23 + local unique_id = "unique_" .. multirand.string(32, "abcdefghijklmnopqrstuvwxyz0123456789") 253.24 + slot.put( 253.25 + '<form', 253.26 + ' id="', unique_id , '"', 253.27 + ' action="', attr_action, '"', 253.28 + ' class="', attr_class, '"', 253.29 + ' target="', attr_target, '"', 253.30 + ' method="post"', 253.31 + '>\n', 253.32 + '<input type="submit" value="', args.label or '', '" />', 253.33 + '</form>', 253.34 + '<script>document.getElementById(\'', unique_id, '\').style.display=\'none\';document.write(\'<a href="#" class="', attr_class , '" onclick="document.getElementById(\\\'', unique_id, '\\\').submit();">' 253.35 + ) 253.36 + if args.icon then 253.37 + ui_deprecated.image{ image = 'ui/icon/' .. args.icon } 253.38 + end 253.39 + if args.image then 253.40 + ui_deprecated.image{ image = args.image } 253.41 + end 253.42 + if args.label then 253.43 + slot.put(args.label) 253.44 + end 253.45 + slot.put("</a>');</script>") 253.46 + else 253.47 + local attr_class = table.concat({ 'ui_link', args.class }, ' ') 253.48 + slot.put( 253.49 + '<a href="', 253.50 + args.url or encode.url{ 253.51 + module = args.module or request.get_module(), 253.52 + view = args.view, 253.53 + id = args.id, 253.54 + params = args.params, 253.55 + }, 253.56 + '" ', 253.57 + ui_deprecated._stringify_table({ class = attr_class }), 253.58 + ui_deprecated._stringify_table( args.html_options or {} ), 253.59 + '>' 253.60 + ) 253.61 + if args.icon then 253.62 + ui_deprecated.image{ image = 'ui/icon/' .. args.icon } 253.63 + end 253.64 + if args.image then 253.65 + ui_deprecated.image{ image = args.image } 253.66 + end 253.67 + if args.label then 253.68 + slot.put(args.label) 253.69 + end 253.70 + slot.put('</a>') 253.71 + end 253.72 +end
254.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 254.2 +++ b/framework/env/ui_deprecated/list.lua Sun Oct 25 12:00:00 2009 +0100 254.3 @@ -0,0 +1,153 @@ 254.4 +-- 254.5 +-- Creates a list view of a collection 254.6 +-- 254.7 +-- Example: 254.8 +-- 254.9 +-- ui_deprecated.list({ 254.10 +-- label = 'Point table', 254.11 +-- collection = mycollection, 254.12 +-- type = 'table' -- 'table', 'ulli' 254.13 +-- cols = { 254.14 +-- { 254.15 +-- label = _'Id', 254.16 +-- field = 'id', 254.17 +-- width = 100, 254.18 +-- link = { module = 'mymodule', view = 'show' }, 254.19 +-- sort = true, 254.20 +-- }, 254.21 +-- { 254.22 +-- label = _'Name', 254.23 +-- field = 'name', 254.24 +-- width = 200, 254.25 +-- link = { module = 'mymodule', view = 'show' }, 254.26 +-- sort = true, 254.27 +-- }, 254.28 +-- { 254.29 +-- label = _'Points', 254.30 +-- func = function(record) 254.31 +-- return record.points_a + record.points_b + record.points_c 254.32 +-- end, 254.33 +-- width = 300 254.34 +-- } 254.35 +-- } 254.36 +-- }) 254.37 +-- 254.38 + 254.39 +function ui_deprecated.list(args) 254.40 + local args = args 254.41 + args.type = args.type or 'table' 254.42 + if args.label then 254.43 + slot.put('<div class="ui_list_label">' .. encode.html(args.label) .. '</div>\n') 254.44 + end 254.45 + if not args or not args.collection or not args.cols then 254.46 + error('No args for ui_deprecated.list_end') 254.47 + end 254.48 + if args.type == 'table' then 254.49 + slot.put('<table class="ui_list">') 254.50 + slot.put('<tr class="ui_list_head">') 254.51 + elseif args.type == 'ulli' then 254.52 + slot.put('<ul class="ui_list">') 254.53 + slot.put('<li class="ui_list_head">') 254.54 + end 254.55 + if args.type == 'table' then 254.56 + elseif args.type == 'ulli' then 254.57 + end 254.58 + for j, col in ipairs(args.cols) do 254.59 + class_string = '' 254.60 + if col.align then 254.61 + class_string = ' class="' .. col.align .. '"' 254.62 + end 254.63 + if args.type == 'table' then 254.64 + slot.put('<th style="width: ' .. col.width .. ';"' .. class_string ..'>') 254.65 + slot.put(ui_deprecated.box({ name = 'ui_list_col_value', content = encode.html(col.label) }) ) 254.66 + slot.put('</th>') 254.67 + elseif args.type == 'ulli' then 254.68 + slot.put('<div style="width: ' .. col.width .. ';"' .. class_string ..'>') 254.69 + slot.put(ui_deprecated.box({ name = 'ui_list_col_value', content = encode.html(col.label) }) ) 254.70 + slot.put('</div>') 254.71 + end 254.72 + end 254.73 + if args.type == 'table' then 254.74 + slot.put('</tr>') 254.75 + elseif args.type == 'ulli' then 254.76 + slot.put('<br style="clear: left;" />') 254.77 + slot.put('</li>') 254.78 + end 254.79 + for i, obj in ipairs(args.collection) do 254.80 + if args.type == 'table' then 254.81 + slot.put('<tr>') 254.82 + elseif args.type == 'ulli' then 254.83 + slot.put('<li>') 254.84 + end 254.85 + for j, col in ipairs(args.cols) do 254.86 + class_string = '' 254.87 + if col.align then 254.88 + class_string = ' class="ui_list_col_align_' .. col.align .. '"' 254.89 + end 254.90 + if args.type == 'table' then 254.91 + slot.put('<td' .. class_string ..'>') 254.92 + elseif args.type == 'ulli' then 254.93 + slot.put('<div style="width: ' .. col.width .. ';"' .. class_string ..'>') 254.94 + end 254.95 + if col.link then 254.96 + local params = {} 254.97 + if col.link then 254.98 + params = col.link.params or {} 254.99 + end 254.100 + if col.link_values then 254.101 + for key, field in pairs(col.link_values) do 254.102 + params[key] = obj[field] 254.103 + end 254.104 + end 254.105 + local id 254.106 + if col.link_id_field then 254.107 + id = obj[col.link_id_field] 254.108 + end 254.109 + local value 254.110 + if col.value then 254.111 + value = col.value 254.112 + else 254.113 + value = obj[col.field] 254.114 + end 254.115 + ui_deprecated.link{ 254.116 + label = convert.to_human(value), 254.117 + module = col.link.module, 254.118 + view = col.link.view, 254.119 + id = id, 254.120 + params = params, 254.121 + } 254.122 + elseif col.link_func then 254.123 + local link = col.link_func(obj) 254.124 + if link then 254.125 + ui_deprecated.link(link) 254.126 + end 254.127 + else 254.128 + local text 254.129 + if col.func then 254.130 + text = col.func(obj) 254.131 + elseif col.value then 254.132 + text = convert.to_human(value) 254.133 + else 254.134 + text = convert.to_human(obj[col.field]) 254.135 + end 254.136 + ui_deprecated.box{ name = 'ui_list_col_value', content = text } 254.137 + end 254.138 + if args.type == 'table' then 254.139 + slot.put('</td>') 254.140 + elseif args.type == 'ulli' then 254.141 + slot.put('</div>') 254.142 + end 254.143 + end 254.144 + if args.type == 'table' then 254.145 + slot.put('</tr>') 254.146 + elseif args.type == 'ulli' then 254.147 + slot.put('<br style="clear: left;" />') 254.148 + slot.put('</li>') 254.149 + end 254.150 + end 254.151 + if args.type == 'table' then 254.152 + slot.put('</table>') 254.153 + elseif args.type == 'ulli' then 254.154 + slot.put('</ul><div style="clear: left"> </div>') 254.155 + end 254.156 +end 254.157 \ No newline at end of file
255.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 255.2 +++ b/framework/env/ui_deprecated/multiselect.lua Sun Oct 25 12:00:00 2009 +0100 255.3 @@ -0,0 +1,50 @@ 255.4 +function ui_deprecated.multiselect(args) 255.5 + local record = assert(slot.get_state_table(), "ui_deprecated.multiselect was not called within a form.").form_record 255.6 + local name = args.name 255.7 + local relationship = args.relationship 255.8 + 255.9 + local foreign_records = relationship.foreign_records 255.10 + 255.11 + local selector = relationship.connected_by_model:new_selector() 255.12 + selector:add_where{ relationship.connected_by_my_id .. ' = ?', record[relationship.my_id] } 255.13 + local connected_by_records = selector:exec() 255.14 + 255.15 + local connections = {} 255.16 + for i, connected_by_record in ipairs(connected_by_records) do 255.17 + connections[connected_by_record[relationship.connected_by_foreign_id]] = connected_by_record 255.18 + end 255.19 + 255.20 + local html = {} 255.21 + 255.22 + if args.type == "checkboxes" then 255.23 + for i, foreign_record in ipairs(foreign_records) do 255.24 + local selected = '' 255.25 + if connections[foreign_record[relationship.foreign_id]] then 255.26 + selected = ' checked="1"' 255.27 + end 255.28 + html[#html + 1] = '<input type="checkbox" name="' .. name .. '" value="' .. foreign_record[relationship.foreign_id] .. '"' .. selected .. '> ' .. convert.to_human(foreign_record[relationship.foreign_name], "string") .. '<br />\n' 255.29 + end 255.30 + 255.31 + else 255.32 + html[#html + 1] = '<select name="' .. name .. '" multiple="1">' 255.33 + for i, foreign_record in ipairs(foreign_records) do 255.34 + local selected = '' 255.35 + if connections[foreign_record[relationship.foreign_id]] then 255.36 + selected = ' selected="1"' 255.37 + end 255.38 + html[#html + 1] = '<option value="' .. foreign_record[relationship.foreign_id] .. '"' .. selected .. '>' .. convert.to_human(foreign_record[relationship.foreign_name], "string") .. '</option>\n' 255.39 + end 255.40 + html[#html + 1] = '</select>' 255.41 + 255.42 + end 255.43 + 255.44 + slot.put('<div class="ui_field ui_multiselect">\n') 255.45 + if args.label then 255.46 + slot.put('<div class="label">', encode.html(args.label), '</div>\n') 255.47 + end 255.48 + slot.put('<div class="value">', 255.49 + table.concat(html), 255.50 + '</div>\n', 255.51 + '</div>\n' 255.52 + ) 255.53 +end 255.54 \ No newline at end of file
256.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 256.2 +++ b/framework/env/ui_deprecated/select.lua Sun Oct 25 12:00:00 2009 +0100 256.3 @@ -0,0 +1,31 @@ 256.4 +function ui_deprecated.select(args) 256.5 + local record = assert(slot.get_state_table(), "ui_deprecated.select was not called within a form.").form_record 256.6 + local value = param.get(args.field) or record[args.field] 256.7 + local html_options = args.html_options or {} 256.8 + html_options.name = args.field 256.9 + 256.10 + ui_deprecated.tag("div", { html_options = { class="ui_field ui_select" }, content = function() 256.11 + if args.label then 256.12 + ui_deprecated.tag("div", { html_options = { class="label" }, content = function() 256.13 + ui_deprecated.text(args.label) 256.14 + end }) 256.15 + end 256.16 + ui_deprecated.tag("div", { html_options = { class="value" }, content = function() 256.17 + ui_deprecated.tag("select", { html_options = html_options, content = function() 256.18 + if args.include_option then 256.19 + ui_deprecated.tag("option", { html_options = { value = "" }, content = args.include_option }) 256.20 + end 256.21 + for i, object in ipairs(args.foreign_records) do 256.22 + local selected = nil 256.23 + if tostring(object[args.foreign_id]) == tostring(value) then 256.24 + selected = "1" 256.25 + end 256.26 + ui_deprecated.tag("option", { html_options = { value = object[args.foreign_id], selected = selected }, content = object[args.foreign_name] }) 256.27 + end 256.28 + end }) -- /select 256.29 + end }) -- /div 256.30 + end }) -- /div 256.31 +end 256.32 + 256.33 + 256.34 +
257.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 257.2 +++ b/framework/env/ui_deprecated/submit.lua Sun Oct 25 12:00:00 2009 +0100 257.3 @@ -0,0 +1,24 @@ 257.4 +-- 257.5 +-- Creates a submit buttom for a form 257.6 +-- 257.7 +-- label (string) The label of the box 257.8 +-- 257.9 +-- Example: 257.10 +-- 257.11 +-- ui_deprecated.submit({ 257.12 +-- label = 'Save' 257.13 +-- }) 257.14 +-- 257.15 + 257.16 +function ui_deprecated.submit(args) 257.17 + local args = args or {} 257.18 + args.label = args.label or 'Submit' 257.19 + slot.put( 257.20 + '<div class="ui_field ui_submit">', 257.21 + '<div class="label"> </div>', 257.22 + '<div class="value">', 257.23 + '<input type="submit" value="', encode.html(args.label), '" />', 257.24 + '</div>', 257.25 + '</div>' 257.26 + ) 257.27 +end
258.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 258.2 +++ b/framework/env/ui_deprecated/tag.lua Sun Oct 25 12:00:00 2009 +0100 258.3 @@ -0,0 +1,20 @@ 258.4 +function ui_deprecated.tag(name, args) 258.5 + 258.6 + local html_options = args.html_options or {} 258.7 + 258.8 + slot.put('<', name, ui_deprecated._stringify_table(html_options), '>') 258.9 + 258.10 + if args.label then 258.11 + ui_deprecated.label(args.label) 258.12 + end 258.13 + 258.14 + if type(args.content) == 'function' then 258.15 + args.content() 258.16 + else 258.17 + ui_deprecated.text(args.content) 258.18 + end 258.19 + 258.20 + slot.put('</', name, '>') 258.21 + 258.22 + 258.23 +end 258.24 \ No newline at end of file
259.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 259.2 +++ b/framework/env/ui_deprecated/text.lua Sun Oct 25 12:00:00 2009 +0100 259.3 @@ -0,0 +1,4 @@ 259.4 + 259.5 +function ui_deprecated.text(value) 259.6 + slot.put(encode.html(convert.to_human(value))) 259.7 +end 259.8 \ No newline at end of file
261.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 261.2 +++ b/libraries/atom/atom.lua Sun Oct 25 12:00:00 2009 +0100 261.3 @@ -0,0 +1,1529 @@ 261.4 +#!/usr/bin/env lua 261.5 + 261.6 +local _G = _G 261.7 +local _VERSION = _VERSION 261.8 +local assert = assert 261.9 +local error = error 261.10 +local getfenv = getfenv 261.11 +local getmetatable = getmetatable 261.12 +local ipairs = ipairs 261.13 +local module = module 261.14 +local next = next 261.15 +local pairs = pairs 261.16 +local print = print 261.17 +local rawequal = rawequal 261.18 +local rawget = rawget 261.19 +local rawset = rawset 261.20 +local require = require 261.21 +local select = select 261.22 +local setfenv = setfenv 261.23 +local setmetatable = setmetatable 261.24 +local tonumber = tonumber 261.25 +local tostring = tostring 261.26 +local type = type 261.27 +local unpack = unpack 261.28 + 261.29 +local coroutine = coroutine 261.30 +local io = io 261.31 +local math = math 261.32 +local os = os 261.33 +local string = string 261.34 +local table = table 261.35 + 261.36 +module(...) 261.37 + 261.38 + 261.39 + 261.40 +--------------------------------------- 261.41 +-- general functions and definitions -- 261.42 +--------------------------------------- 261.43 + 261.44 +--[[-- 261.45 +bool = -- true, if value is an integer within resolution 261.46 +atom.is_integer( 261.47 + value -- value to be tested 261.48 +) 261.49 + 261.50 +This function returns true if the given object is an integer within resolution. 261.51 + 261.52 +--]]-- 261.53 +function is_integer(i) 261.54 + return 261.55 + type(i) == "number" and i % 1 == 0 and 261.56 + (i + 1) - i == 1 and i - (i - 1) == 1 261.57 +end 261.58 +--//-- 261.59 + 261.60 +--[[-- 261.61 +atom.not_a_number 261.62 + 261.63 +Value representing an invalid numeric result. Used for atom.integer.invalid and atom.number.invalid. 261.64 + 261.65 +--]]-- 261.66 +not_a_number = 0 / 0 261.67 +--//-- 261.68 + 261.69 +do 261.70 + 261.71 + local shadow = setmetatable({}, { __mode = "k" }) 261.72 + 261.73 + local type_mt = { __index = {} } 261.74 + 261.75 + function type_mt:__call(...) 261.76 + return self:new(...) 261.77 + end 261.78 + 261.79 + function type_mt.__index:_create(data) 261.80 + local value = setmetatable({}, self) 261.81 + shadow[value] = data 261.82 + return value 261.83 + end 261.84 + 261.85 + local function write_prohibited() 261.86 + error("Modification of an atom is prohibited.") 261.87 + end 261.88 + 261.89 + -- returns a new type as a table, which serves also as metatable 261.90 + function create_new_type(name) 261.91 + local t = setmetatable( 261.92 + { methods = {}, getters = {}, name = name }, 261.93 + type_mt 261.94 + ) 261.95 + function t.__index(self, key) 261.96 + local data = shadow[self] 261.97 + local value = data[key] 261.98 + if value ~= nil then return value end 261.99 + local method = t.methods[key] 261.100 + if method then return method end 261.101 + local getter = t.getters[key] 261.102 + if getter then return getter(self) end 261.103 + end 261.104 + t.__newindex = write_prohibited 261.105 + return t 261.106 + end 261.107 + 261.108 + --[[-- 261.109 + bool = -- true, if 'value' is of type 't' 261.110 + atom.has_type( 261.111 + value, -- any value 261.112 + t -- atom time, e.g. atom.date, or lua type, e.g. "string" 261.113 + ) 261.114 + 261.115 + This function checks, if a value is of a given type. The value may be an invalid value though, e.g. atom.date.invalid. 261.116 + 261.117 + --]]-- 261.118 + function has_type(value, t) 261.119 + if t == nil then error("No type passed to has_type(...) function.") end 261.120 + local lua_type = type(value) 261.121 + return 261.122 + lua_type == t or 261.123 + getmetatable(value) == t or 261.124 + (lua_type == "boolean" and t == _M.boolean) or 261.125 + (lua_type == "string" and t == _M.string) or ( 261.126 + lua_type == "number" and 261.127 + (t == _M.number or ( 261.128 + t == _M.integer and ( 261.129 + not (value <= 0 or value >= 0) or ( 261.130 + value % 1 == 0 and 261.131 + (value + 1) - value == 1 and 261.132 + value - (value - 1) == 1 261.133 + ) 261.134 + ) 261.135 + )) 261.136 + ) 261.137 + end 261.138 + --//-- 261.139 + 261.140 + --[[-- 261.141 + bool = -- true, if 'value' is of type 't' 261.142 + atom.is_valid( 261.143 + value, -- any value 261.144 + t -- atom time, e.g. atom.date, or lua type, e.g. "string" 261.145 + ) 261.146 + 261.147 + This function checks, if a value is valid. It optionally checks, if the value is of a given type. 261.148 + 261.149 + --]]-- 261.150 + function is_valid(value, t) 261.151 + local lua_type = type(value) 261.152 + if lua_type == "table" then 261.153 + local mt = getmetatable(value) 261.154 + if t then 261.155 + return t == mt and not value.invalid 261.156 + else 261.157 + return (getmetatable(mt) == type_mt) and not value.invalid 261.158 + end 261.159 + elseif lua_type == "boolean" then 261.160 + return not t or t == "boolean" or t == _M.boolean 261.161 + elseif lua_type == "string" then 261.162 + return not t or t == "string" or t == _M.string 261.163 + elseif lua_type == "number" then 261.164 + if t == _M.integer then 261.165 + return 261.166 + value % 1 == 0 and 261.167 + (value + 1) - value == 1 and 261.168 + value - (value - 1) == 1 261.169 + else 261.170 + return 261.171 + (not t or t == "number" or t == _M.number) and 261.172 + (value <= 0 or value >= 0) 261.173 + end 261.174 + else 261.175 + return false 261.176 + end 261.177 + end 261.178 + --//-- 261.179 + 261.180 +end 261.181 + 261.182 +--[[-- 261.183 +string = -- string representation to be passed to a load function 261.184 +atom.dump( 261.185 + value -- value to be dumped 261.186 +) 261.187 + 261.188 +This function returns a string representation of the given value. 261.189 + 261.190 +--]]-- 261.191 +function dump(obj) 261.192 + if obj == nil then 261.193 + return "" 261.194 + else 261.195 + return tostring(obj) 261.196 + end 261.197 +end 261.198 +--//-- 261.199 + 261.200 + 261.201 + 261.202 +------------- 261.203 +-- boolean -- 261.204 +------------- 261.205 + 261.206 +boolean = { name = "boolean" } 261.207 + 261.208 +--[[-- 261.209 +bool = -- true, false, or nil 261.210 +atom.boolean:load( 261.211 + string -- string to be interpreted as boolean 261.212 +) 261.213 + 261.214 +This method returns true or false or nil, depending on the input string. 261.215 + 261.216 +--]]-- 261.217 +function boolean:load(str) 261.218 + if type(str) ~= "string" then 261.219 + error("String expected") 261.220 + elseif str == "" then 261.221 + return nil 261.222 + elseif string.find(str, "^[TtYy1]") then 261.223 + return true 261.224 + elseif string.find(str, "^[FfNn0]") then 261.225 + return false 261.226 + else 261.227 + return nil -- we don't have an undefined bool 261.228 + end 261.229 +end 261.230 +--//-- 261.231 + 261.232 + 261.233 + 261.234 +------------ 261.235 +-- string -- 261.236 +------------ 261.237 + 261.238 +_M.string = { name = "string" } 261.239 + 261.240 +--[[-- 261.241 +string = -- the same string 261.242 +atom.string:load( 261.243 + string -- a string 261.244 +) 261.245 + 261.246 +This method returns the passed string, or throws an error, if the passed argument is not a string. 261.247 + 261.248 +--]]-- 261.249 +function _M.string:load(str) 261.250 + if type(str) ~= "string" then 261.251 + error("String expected") 261.252 + else 261.253 + return str 261.254 + end 261.255 +end 261.256 +--//-- 261.257 + 261.258 + 261.259 + 261.260 +------------- 261.261 +-- integer -- 261.262 +------------- 261.263 + 261.264 +integer = { name = "integer" } 261.265 + 261.266 +--[[-- 261.267 +int = -- an integer or atom.integer.invalid (atom.not_a_number) 261.268 +atom.integer:load( 261.269 + string -- a string representing an integer 261.270 +) 261.271 + 261.272 +This method returns an integer represented by the given string. If the string doesn't represent a valid integer, then not-a-number is returned. 261.273 + 261.274 +--]]-- 261.275 +function integer:load(str) 261.276 + if type(str) ~= "string" then 261.277 + error("String expected") 261.278 + elseif str == "" then 261.279 + return nil 261.280 + else 261.281 + local num = tonumber(str) 261.282 + if is_integer(num) then return num else return not_a_number end 261.283 + end 261.284 +end 261.285 +--//-- 261.286 + 261.287 +--[[-- 261.288 +atom.integer.invalid 261.289 + 261.290 +This represents an invalid integer. 261.291 + 261.292 +--]]-- 261.293 +integer.invalid = not_a_number 261.294 +--// 261.295 + 261.296 + 261.297 + 261.298 +------------ 261.299 +-- number -- 261.300 +------------ 261.301 + 261.302 +number = create_new_type("number") 261.303 + 261.304 +--[[-- 261.305 +int = -- a number or atom.number.invalid (atom.not_a_number) 261.306 +atom.number:load( 261.307 + string -- a string representing a number 261.308 +) 261.309 + 261.310 +This method returns a number represented by the given string. If the string doesn't represent a valid number, then not-a-number is returned. 261.311 + 261.312 +--]]-- 261.313 +function number:load(str) 261.314 + if type(str) ~= "string" then 261.315 + error("String expected") 261.316 + elseif str == "" then 261.317 + return nil 261.318 + else 261.319 + return tonumber(str) or not_a_number 261.320 + end 261.321 +end 261.322 +--//-- 261.323 + 261.324 +--[[-- 261.325 +atom.number.invalid 261.326 + 261.327 +This represents an invalid number. 261.328 + 261.329 +--]]-- 261.330 +number.invalid = not_a_number 261.331 +--//-- 261.332 + 261.333 + 261.334 + 261.335 +-------------- 261.336 +-- fraction -- 261.337 +-------------- 261.338 + 261.339 +fraction = create_new_type("fraction") 261.340 + 261.341 +--[[-- 261.342 +i = -- the greatest common divisor (GCD) of all given natural numbers 261.343 +atom.gcd( 261.344 + a, -- a natural number 261.345 + b, -- another natural number 261.346 + ... -- optionally more natural numbers 261.347 +) 261.348 + 261.349 +This function returns the greatest common divisor (GCD) of two or more natural numbers. 261.350 + 261.351 +--]]-- 261.352 +function gcd(a, b, ...) 261.353 + if a % 1 ~= 0 or a <= 0 then return 0 / 0 end 261.354 + if b == nil then 261.355 + return a 261.356 + else 261.357 + if b % 1 ~= 0 or b <= 0 then return 0 / 0 end 261.358 + if ... == nil then 261.359 + local k = 0 261.360 + local t 261.361 + while a % 2 == 0 and b % 2 == 0 do 261.362 + a = a / 2; b = b / 2; k = k + 1 261.363 + end 261.364 + if a % 2 == 0 then t = a else t = -b end 261.365 + while t ~= 0 do 261.366 + while t % 2 == 0 do t = t / 2 end 261.367 + if t > 0 then a = t else b = -t end 261.368 + t = a - b 261.369 + end 261.370 + return a * 2 ^ k 261.371 + else 261.372 + return gcd(gcd(a, b), ...) 261.373 + end 261.374 + end 261.375 +end 261.376 +--//-- 261.377 + 261.378 +--[[-- 261.379 +i = --the least common multiple (LCD) of all given natural numbers 261.380 +atom.lcm( 261.381 + a, -- a natural number 261.382 + b, -- another natural number 261.383 + ... -- optionally more natural numbers 261.384 +) 261.385 + 261.386 +This function returns the least common multiple (LCD) of two or more natural numbers. 261.387 + 261.388 +--]]-- 261.389 +function lcm(a, b, ...) 261.390 + if a % 1 ~= 0 or a <= 0 then return 0 / 0 end 261.391 + if b == nil then 261.392 + return a 261.393 + else 261.394 + if b % 1 ~= 0 or b <= 0 then return 0 / 0 end 261.395 + if ... == nil then 261.396 + return a * b / gcd(a, b) 261.397 + else 261.398 + return lcm(lcm(a, b), ...) 261.399 + end 261.400 + end 261.401 +end 261.402 +--//-- 261.403 + 261.404 +--[[-- 261.405 +atom.fraction.invalid 261.406 + 261.407 +Value representing an invalid fraction. 261.408 + 261.409 +--]]-- 261.410 +fraction.invalid = fraction:_create{ 261.411 + numerator = not_a_number, denominator = not_a_number, invalid = true 261.412 +} 261.413 +--//-- 261.414 + 261.415 +--[[-- 261.416 +frac = -- fraction 261.417 +atom.fraction:new( 261.418 + numerator, -- numerator 261.419 + denominator -- denominator 261.420 +) 261.421 + 261.422 +This method creates a new fraction. 261.423 + 261.424 +--]]-- 261.425 +function fraction:new(numerator, denominator) 261.426 + if not ( 261.427 + (numerator == nil or type(numerator) == "number") and 261.428 + (denominator == nil or type(denominator) == "number") 261.429 + ) then 261.430 + error("Invalid arguments passed to fraction constructor.") 261.431 + elseif 261.432 + (not is_integer(numerator)) or 261.433 + (denominator and (not is_integer(denominator))) 261.434 + then 261.435 + return fraction.invalid 261.436 + elseif denominator then 261.437 + if denominator == 0 then 261.438 + return fraction.invalid 261.439 + elseif numerator == 0 then 261.440 + return fraction:_create{ numerator = 0, denominator = 1, float = 0 } 261.441 + else 261.442 + local d = gcd(math.abs(numerator), math.abs(denominator)) 261.443 + if denominator < 0 then d = -d end 261.444 + local numerator2, denominator2 = numerator / d, denominator / d 261.445 + return fraction:_create{ 261.446 + numerator = numerator2, 261.447 + denominator = denominator2, 261.448 + float = numerator2 / denominator2 261.449 + } 261.450 + end 261.451 + else 261.452 + return fraction:_create{ 261.453 + numerator = numerator, denominator = 1, float = numerator 261.454 + } 261.455 + end 261.456 +end 261.457 +--//-- 261.458 + 261.459 +--[[-- 261.460 +frac = -- fraction represented by the given string 261.461 +atom.fraction:load( 261.462 + string -- string representation of a fraction 261.463 +) 261.464 + 261.465 +This method returns a fraction represented by the given string. 261.466 + 261.467 +--]]-- 261.468 +function fraction:load(str) 261.469 + if str == "" then 261.470 + return nil 261.471 + else 261.472 + local sign, int = string.match(str, "^(%-?)([0-9]+)$") 261.473 + if sign == "" then return fraction:new(tonumber(int)) 261.474 + elseif sign == "-" then return fraction:new(- tonumber(int)) 261.475 + end 261.476 + local sign, n, d = string.match(str, "^(%-?)([0-9]+)/([0-9]+)$") 261.477 + if sign == "" then return fraction:new(tonumber(n), tonumber(d)) 261.478 + elseif sign == "-" then return fraction:new(- tonumber(n), tonumber(d)) 261.479 + end 261.480 + return fraction.invalid 261.481 + end 261.482 +end 261.483 +--//-- 261.484 + 261.485 +function fraction:__tostring() 261.486 + if self.invalid then 261.487 + return "not_a_fraction" 261.488 + else 261.489 + return self.numerator .. "/" .. self.denominator 261.490 + end 261.491 +end 261.492 + 261.493 +function fraction.__eq(value1, value2) 261.494 + if value1.invalid or value2.invalid then 261.495 + return false 261.496 + else 261.497 + return 261.498 + value1.numerator == value2.numerator and 261.499 + value1.denominator == value2.denominator 261.500 + end 261.501 +end 261.502 + 261.503 +function fraction.__lt(value1, value2) 261.504 + if value1.invalid or value2.invalid then 261.505 + return false 261.506 + else 261.507 + return value1.float < value2.float 261.508 + end 261.509 +end 261.510 + 261.511 +function fraction.__le(value1, value2) 261.512 + if value1.invalid or value2.invalid then 261.513 + return false 261.514 + else 261.515 + return value1.float <= value2.float 261.516 + end 261.517 +end 261.518 + 261.519 +function fraction:__unm() 261.520 + return fraction(-self.numerator, self.denominator) 261.521 +end 261.522 + 261.523 +do 261.524 + 261.525 + local function extract(value1, value2) 261.526 + local n1, d1, n2, d2 261.527 + if getmetatable(value1) == fraction then 261.528 + n1 = value1.numerator 261.529 + d1 = value1.denominator 261.530 + elseif type(value1) == "number" then 261.531 + n1 = value1 261.532 + d1 = 1 261.533 + else 261.534 + error("Left operand of operator has wrong type.") 261.535 + end 261.536 + if getmetatable(value2) == fraction then 261.537 + n2 = value2.numerator 261.538 + d2 = value2.denominator 261.539 + elseif type(value2) == "number" then 261.540 + n2 = value2 261.541 + d2 = 1 261.542 + else 261.543 + error("Right operand of operator has wrong type.") 261.544 + end 261.545 + return n1, d1, n2, d2 261.546 + end 261.547 + 261.548 + function fraction.__add(value1, value2) 261.549 + local n1, d1, n2, d2 = extract(value1, value2) 261.550 + return fraction(n1 * d2 + n2 * d1, d1 * d2) 261.551 + end 261.552 + 261.553 + function fraction.__sub(value1, value2) 261.554 + local n1, d1, n2, d2 = extract(value1, value2) 261.555 + return fraction(n1 * d2 - n2 * d1, d1 * d2) 261.556 + end 261.557 + 261.558 + function fraction.__mul(value1, value2) 261.559 + local n1, d1, n2, d2 = extract(value1, value2) 261.560 + return fraction(n1 * n2, d1 * d2) 261.561 + end 261.562 + 261.563 + function fraction.__div(value1, value2) 261.564 + local n1, d1, n2, d2 = extract(value1, value2) 261.565 + return fraction(n1 * d2, d1 * n2) 261.566 + end 261.567 + 261.568 + function fraction.__pow(value1, value2) 261.569 + local n1, d1, n2, d2 = extract(value1, value2) 261.570 + local n1_abs = math.abs(n1) 261.571 + local d1_abs = math.abs(d1) 261.572 + local n2_abs = math.abs(n2) 261.573 + local d2_abs = math.abs(d2) 261.574 + local numerator, denominator 261.575 + if d2_abs == 1 then 261.576 + numerator = n1_abs 261.577 + denominator = d1_abs 261.578 + else 261.579 + numerator = 0 261.580 + while true do 261.581 + local t = numerator ^ d2_abs 261.582 + if t == n1_abs then break end 261.583 + if not (t < n1_abs) then return value1.float / value2.float end 261.584 + numerator = numerator + 1 261.585 + end 261.586 + denominator = 1 261.587 + while true do 261.588 + local t = denominator ^ d2_abs 261.589 + if t == d1_abs then break end 261.590 + if not (t < d1_abs) then return value1.float / value2.float end 261.591 + denominator = denominator + 1 261.592 + end 261.593 + end 261.594 + if n1 < 0 then 261.595 + if d2_abs % 2 == 1 then 261.596 + numerator = -numerator 261.597 + else 261.598 + return fraction.invalid 261.599 + end 261.600 + end 261.601 + if n2 < 0 then 261.602 + numerator, denominator = denominator, numerator 261.603 + end 261.604 + return fraction(numerator ^ n2_abs, denominator ^ n2_abs) 261.605 + end 261.606 + 261.607 +end 261.608 + 261.609 + 261.610 + 261.611 +---------- 261.612 +-- date -- 261.613 +---------- 261.614 + 261.615 +date = create_new_type("date") 261.616 + 261.617 +do 261.618 + local c1 = 365 -- days of a non-leap year 261.619 + local c4 = 4 * c1 + 1 -- days of a full 4 year cycle 261.620 + local c100 = 25 * c4 - 1 -- days of a full 100 year cycle 261.621 + local c400 = 4 * c100 + 1 -- days of a full 400 year cycle 261.622 + local get_month_offset -- function returning days elapsed within 261.623 + -- the given year until the given month 261.624 + -- (exclusive the given month) 261.625 + do 261.626 + local normal_month_offsets = {} 261.627 + local normal_month_lengths = { 261.628 + 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 261.629 + } 261.630 + local sum = 0 261.631 + for i = 1, 12 do 261.632 + normal_month_offsets[i] = sum 261.633 + sum = sum + normal_month_lengths[i] 261.634 + end 261.635 + function get_month_offset(year, month) 261.636 + if 261.637 + (((year % 4 == 0) and not (year % 100 == 0)) or (year % 400 == 0)) 261.638 + and month > 2 261.639 + then 261.640 + return normal_month_offsets[month] + 1 261.641 + else 261.642 + return normal_month_offsets[month] 261.643 + end 261.644 + end 261.645 + end 261.646 + 261.647 + --[[-- 261.648 + jd = -- days from January 1st 1970 261.649 + atom.date.ymd_to_jd( 261.650 + year, -- year 261.651 + month, -- month from 1 to 12 261.652 + day -- day from 1 to 31 261.653 + ) 261.654 + 261.655 + This function calculates the days from January 1st 1970 for a given year, month and day. 261.656 + 261.657 + --]]-- 261.658 + local offset = 0 261.659 + function date.ymd_to_jd(year, month, day) 261.660 + assert(is_integer(year), "Invalid year specified.") 261.661 + assert(is_integer(month), "Invalid month specified.") 261.662 + assert(is_integer(day), "Invalid day specified.") 261.663 + local calc_year = year - 1 261.664 + local n400 = math.floor(calc_year / 400) 261.665 + local r400 = calc_year % 400 261.666 + local n100 = math.floor(r400 / 100) 261.667 + local r100 = r400 % 100 261.668 + local n4 = math.floor(r100 / 4) 261.669 + local n1 = r100 % 4 261.670 + local jd = ( 261.671 + c400 * n400 + c100 * n100 + c4 * n4 + c1 * n1 + 261.672 + get_month_offset(year, month) + (day - 1) 261.673 + ) 261.674 + return jd - offset 261.675 + end 261.676 + offset = date.ymd_to_jd(1970, 1, 1) 261.677 + --//-- 261.678 + 261.679 + --[[-- 261.680 + year, -- year 261.681 + month, -- month from 1 to 12 261.682 + day = -- day from 1 to 31 261.683 + atom.date.jd_to_ymd( 261.684 + jd, -- days from January 1st 1970 261.685 + ) 261.686 + 261.687 + Given the days from January 1st 1970 this function returns year, month and day. 261.688 + 261.689 + --]]-- 261.690 + function date.jd_to_ymd(jd) 261.691 + assert(is_integer(jd), "Invalid julian date specified.") 261.692 + local calc_jd = jd + offset 261.693 + assert(is_integer(calc_jd), "Julian date is out of range.") 261.694 + local n400 = math.floor(calc_jd / c400) 261.695 + local r400 = calc_jd % c400 261.696 + local n100 = math.floor(r400 / c100) 261.697 + local r100 = r400 % c100 261.698 + if n100 == 4 then n100, r100 = 3, c100 end 261.699 + local n4 = math.floor(r100 / c4) 261.700 + local r4 = r100 % c4 261.701 + local n1 = math.floor(r4 / c1) 261.702 + local r1 = r4 % c1 261.703 + if n1 == 4 then n1, r1 = 3, c1 end 261.704 + local year = 1 + 400 * n400 + 100 * n100 + 4 * n4 + n1 261.705 + local month = 1 + math.floor(r1 / 31) 261.706 + local month_offset = get_month_offset(year, month) 261.707 + if month < 12 then 261.708 + local next_month_offset = get_month_offset(year, month + 1) 261.709 + if r1 >= next_month_offset then 261.710 + month = month + 1 261.711 + month_offset = next_month_offset 261.712 + end 261.713 + end 261.714 + local day = 1 + r1 - month_offset 261.715 + return year, month, day 261.716 + end 261.717 + --//-- 261.718 +end 261.719 + 261.720 +--[[-- 261.721 +atom.date.invalid 261.722 + 261.723 +Value representing an invalid date. 261.724 + 261.725 +--]]-- 261.726 +date.invalid = date:_create{ 261.727 + jd = not_a_number, 261.728 + year = not_a_number, month = not_a_number, day = not_a_number, 261.729 + invalid = true 261.730 +} 261.731 +--//-- 261.732 + 261.733 +--[[-- 261.734 +d = -- date based on the given data 261.735 +atom.date:new{ 261.736 + jd = jd, -- days since January 1st 1970 261.737 + year = year, -- year 261.738 + month = month, -- month from 1 to 12 261.739 + day = day, -- day from 1 to 31 261.740 + iso_weekyear = iso_weekyear, -- year according to ISO 8601 261.741 + iso_week = iso_week, -- week number according to ISO 8601 261.742 + iso_weekday = iso_weekday, -- day of week from 1 for monday to 7 for sunday 261.743 + us_weekyear = us_weekyear, -- year 261.744 + us_week = us_week, -- week number according to US style counting 261.745 + us_weekday = us_weekday -- day of week from 1 for sunday to 7 for saturday 261.746 +} 261.747 + 261.748 +This method returns a new date value, based on given data. 261.749 + 261.750 +--]]-- 261.751 +function date:new(args) 261.752 + local args = args 261.753 + if type(args) == "number" then args = { jd = args } end 261.754 + if type(args) == "table" then 261.755 + local year, month, day = args.year, args.month, args.day 261.756 + local jd = args.jd 261.757 + local iso_weekyear = args.iso_weekyear 261.758 + local iso_week = args.iso_week 261.759 + local iso_weekday = args.iso_weekday 261.760 + local us_week = args.us_week 261.761 + local us_weekday = args.us_weekday 261.762 + if 261.763 + type(year) == "number" and 261.764 + type(month) == "number" and 261.765 + type(day) == "number" 261.766 + then 261.767 + if 261.768 + is_integer(year) and year >= 1 and year <= 9999 and 261.769 + is_integer(month) and month >= 1 and month <= 12 and 261.770 + is_integer(day) and day >= 1 and day <= 31 261.771 + then 261.772 + return date:_create{ 261.773 + jd = date.ymd_to_jd(year, month, day), 261.774 + year = year, month = month, day = day 261.775 + } 261.776 + else 261.777 + return date.invalid 261.778 + end 261.779 + elseif type(jd) == "number" then 261.780 + if is_integer(jd) and jd >= -719162 and jd <= 2932896 then 261.781 + local year, month, day = date.jd_to_ymd(jd) 261.782 + return date:_create{ 261.783 + jd = jd, year = year, month = month, day = day 261.784 + } 261.785 + else 261.786 + return date.invalid 261.787 + end 261.788 + elseif 261.789 + type(year) == "number" and not iso_weekyear and 261.790 + type(iso_week) == "number" and 261.791 + type(iso_weekday) == "number" 261.792 + then 261.793 + if 261.794 + is_integer(year) and 261.795 + is_integer(iso_week) and iso_week >= 0 and iso_week <= 53 and 261.796 + is_integer(iso_weekday) and iso_weekday >= 1 and iso_weekday <= 7 261.797 + then 261.798 + local jan4 = date{ year = year, month = 1, day = 4 } 261.799 + local reference = jan4 - jan4.iso_weekday - 7 -- Sun. of week -1 261.800 + return date(reference + 7 * iso_week + iso_weekday) 261.801 + else 261.802 + return date.invalid 261.803 + end 261.804 + elseif 261.805 + type(iso_weekyear) == "number" and not year and 261.806 + type(iso_week) == "number" and 261.807 + type(iso_weekday) == "number" 261.808 + then 261.809 + if 261.810 + is_integer(iso_weekyear) and 261.811 + is_integer(iso_week) and iso_week >= 0 and iso_week <= 53 and 261.812 + is_integer(iso_weekday) and iso_weekday >= 1 and iso_weekday <= 7 261.813 + then 261.814 + local guessed = date{ 261.815 + year = iso_weekyear, 261.816 + iso_week = iso_week, 261.817 + iso_weekday = iso_weekday 261.818 + } 261.819 + if guessed.invalid or guessed.iso_weekyear == iso_weekyear then 261.820 + return guessed 261.821 + else 261.822 + local year 261.823 + if iso_week <= 1 then 261.824 + year = iso_weekyear - 1 261.825 + elseif iso_week >= 52 then 261.826 + year = iso_weekyear + 1 261.827 + else 261.828 + error("Internal error in ISO week computation occured.") 261.829 + end 261.830 + return date{ 261.831 + year = year, iso_week = iso_week, iso_weekday = iso_weekday 261.832 + } 261.833 + end 261.834 + else 261.835 + return date.invalid 261.836 + end 261.837 + elseif 261.838 + type(year) == "number" and 261.839 + type(us_week) == "number" and 261.840 + type(us_weekday) == "number" 261.841 + then 261.842 + if 261.843 + is_integer(year) and 261.844 + is_integer(us_week) and us_week >= 0 and us_week <= 54 and 261.845 + is_integer(us_weekday) and us_weekday >= 1 and us_weekday <= 7 261.846 + then 261.847 + local jan1 = date{ year = year, month = 1, day = 1 } 261.848 + local reference = jan1 - jan1.us_weekday - 7 -- Sat. of week -1 261.849 + return date(reference + 7 * us_week + us_weekday) 261.850 + else 261.851 + return date.invalid 261.852 + end 261.853 + end 261.854 + end 261.855 + error("Illegal arguments passed to date constructor.") 261.856 +end 261.857 +--//-- 261.858 + 261.859 +--[[-- 261.860 +atom.date:get_current() 261.861 + 261.862 +This function returns today's date. 261.863 + 261.864 +--]]-- 261.865 +function date:get_current() 261.866 + local now = os.date("*t") 261.867 + return date{ 261.868 + year = now.year, month = now.month, day = now.day 261.869 + } 261.870 +end 261.871 +--//-- 261.872 + 261.873 +--[[-- 261.874 +date = -- date represented by the string 261.875 +atom.date:load( 261.876 + string -- string representing a date 261.877 +) 261.878 + 261.879 +This method returns a date represented by the given string. 261.880 + 261.881 +--]]-- 261.882 +function date:load(str) 261.883 + if str == "" then 261.884 + return nil 261.885 + else 261.886 + local year, month, day = string.match( 261.887 + str, "^([0-9][0-9][0-9][0-9])-([0-9][0-9])-([0-9][0-9])$" 261.888 + ) 261.889 + if year then 261.890 + return date{ 261.891 + year = tonumber(year), 261.892 + month = tonumber(month), 261.893 + day = tonumber(day) 261.894 + } 261.895 + else 261.896 + return date.invalid 261.897 + end 261.898 + end 261.899 +end 261.900 +--//-- 261.901 + 261.902 +function date:__tostring() 261.903 + if self.invalid then 261.904 + return "invalid_date" 261.905 + else 261.906 + return string.format( 261.907 + "%04i-%02i-%02i", self.year, self.month, self.day 261.908 + ) 261.909 + end 261.910 +end 261.911 + 261.912 +function date.getters:midnight() 261.913 + return time{ year = self.year, month = self.month, day = self.day } 261.914 +end 261.915 + 261.916 +function date.getters:midday() 261.917 + return time{ 261.918 + year = self.year, month = self.month, day = self.day, 261.919 + hour = 12 261.920 + } 261.921 +end 261.922 + 261.923 +function date.getters:iso_weekday() -- 1 = Monday 261.924 + return (self.jd + 3) % 7 + 1 261.925 +end 261.926 + 261.927 +function date.getters:us_weekday() -- 1 = Sunday 261.928 + return (self.jd + 4) % 7 + 1 261.929 +end 261.930 + 261.931 +function date.getters:iso_weekyear() -- ISO week-numbering year 261.932 + local year, month, day = self.year, self.month, self.day 261.933 + local iso_weekday = self.iso_weekday 261.934 + if month == 1 then 261.935 + if 261.936 + (day == 3 and iso_weekday == 7) or 261.937 + (day == 2 and iso_weekday >= 6) or 261.938 + (day == 1 and iso_weekday >= 5) 261.939 + then 261.940 + return year - 1 261.941 + end 261.942 + elseif month == 12 then 261.943 + if 261.944 + (day == 29 and iso_weekday == 1) or 261.945 + (day == 30 and iso_weekday <= 2) or 261.946 + (day == 31 and iso_weekday <= 3) 261.947 + then 261.948 + return year + 1 261.949 + end 261.950 + end 261.951 + return year 261.952 +end 261.953 + 261.954 +function date.getters:iso_week() 261.955 + local jan4 = date{ year = self.iso_weekyear, month = 1, day = 4 } 261.956 + local reference = jan4.jd - jan4.iso_weekday - 6 -- monday of week 0 261.957 + return math.floor((self.jd - reference) / 7) 261.958 +end 261.959 + 261.960 +function date.getters:us_week() 261.961 + local jan1 = date{ year = self.year, month = 1, day = 1 } 261.962 + local reference = jan1.jd - jan1.us_weekday - 6 -- sunday of week 0 261.963 + return math.floor((self.jd - reference) / 7) 261.964 +end 261.965 + 261.966 +function date.__eq(value1, value2) 261.967 + if value1.invalid or value2.invalid then 261.968 + return false 261.969 + else 261.970 + return value1.jd == value2.jd 261.971 + end 261.972 +end 261.973 + 261.974 +function date.__lt(value1, value2) 261.975 + if value1.invalid or value2.invalid then 261.976 + return false 261.977 + else 261.978 + return value1.jd < value2.jd 261.979 + end 261.980 +end 261.981 + 261.982 +function date.__le(value1, value2) 261.983 + if value1.invalid or value2.invalid then 261.984 + return false 261.985 + else 261.986 + return value1.jd <= value2.jd 261.987 + end 261.988 +end 261.989 + 261.990 +function date.__add(value1, value2) 261.991 + if getmetatable(value1) == date then 261.992 + if getmetatable(value2) == date then 261.993 + error("Can not add two dates.") 261.994 + elseif type(value2) == "number" then 261.995 + return date(value1.jd + value2) 261.996 + else 261.997 + error("Right operand of '+' operator has wrong type.") 261.998 + end 261.999 + elseif type(value1) == "number" then 261.1000 + if getmetatable(value2) == date then 261.1001 + return date(value1 + value2.jd) 261.1002 + else 261.1003 + error("Assertion failed") 261.1004 + end 261.1005 + else 261.1006 + error("Left operand of '+' operator has wrong type.") 261.1007 + end 261.1008 +end 261.1009 + 261.1010 +function date.__sub(value1, value2) 261.1011 + if not getmetatable(value1) == date then 261.1012 + error("Left operand of '-' operator has wrong type.") 261.1013 + end 261.1014 + if getmetatable(value2) == date then 261.1015 + return value1.jd - value2.jd -- TODO: transform to interval 261.1016 + elseif type(value2) == "number" then 261.1017 + return date(value1.jd - value2) 261.1018 + else 261.1019 + error("Right operand of '-' operator has wrong type.") 261.1020 + end 261.1021 +end 261.1022 + 261.1023 + 261.1024 + 261.1025 +--------------- 261.1026 +-- timestamp -- 261.1027 +--------------- 261.1028 + 261.1029 +timestamp = create_new_type("timestamp") 261.1030 + 261.1031 +--[[-- 261.1032 +tsec = -- seconds since January 1st 1970 00:00 261.1033 +atom.timestamp.ymdhms_to_tsec( 261.1034 + year, -- year 261.1035 + month, -- month from 1 to 12 261.1036 + day, -- day from 1 to 31 261.1037 + hour, -- hour from 0 to 23 261.1038 + minute, -- minute from 0 to 59 261.1039 + second -- second from 0 to 59 261.1040 +) 261.1041 + 261.1042 +Given the year, month, day, hour, minute and second, this function returns the number of seconds since January 1st 1970 00:00. 261.1043 + 261.1044 +--]]-- 261.1045 +function timestamp.ymdhms_to_tsec(year, month, day, hour, minute, second) 261.1046 + return 261.1047 + 86400 * date.ymd_to_jd(year, month, day) + 261.1048 + 3600 * hour + 60 * minute + second 261.1049 +end 261.1050 +--//-- 261.1051 + 261.1052 +--[[-- 261.1053 +year, -- year 261.1054 +month, -- month from 1 to 12 261.1055 +day, -- day from 1 to 31 261.1056 +hour, -- hour from 0 to 23 261.1057 +minute, -- minute from 0 to 59 261.1058 +second = -- second from 0 to 59 261.1059 +atom.timestamp.tsec_to_ymdhms( 261.1060 + tsec -- seconds since January 1st 1970 00:00 261.1061 +) 261.1062 + 261.1063 +Given the seconds since January 1st 1970 00:00, this function returns the year, month, day, hour, minute and second. 261.1064 + 261.1065 +--]]-- 261.1066 +function timestamp.tsec_to_ymdhms(tsec) 261.1067 + local jd = math.floor(tsec / 86400) 261.1068 + local dsec = tsec % 86400 261.1069 + local year, month, day = date.jd_to_ymd(jd) 261.1070 + local hour = math.floor(dsec / 3600) 261.1071 + local minute = math.floor((dsec % 3600) / 60) 261.1072 + local second = dsec % 60 261.1073 + return year, month, day, hour, minute, second 261.1074 +end 261.1075 +--//-- 261.1076 + 261.1077 +--[[-- 261.1078 +timestamp.invalid 261.1079 + 261.1080 +Value representing an invalid timestamp. 261.1081 + 261.1082 +--]]-- 261.1083 +timestamp.invalid = timestamp:_create{ 261.1084 + tsec = not_a_number, 261.1085 + year = not_a_number, month = not_a_number, day = not_a_number, 261.1086 + hour = not_a_number, minute = not_a_number, second = not_a_number, 261.1087 + invalid = true 261.1088 +} 261.1089 +--//-- 261.1090 + 261.1091 +--[[-- 261.1092 +ts = -- timestamp based on given data 261.1093 +atom.timestamp:new{ 261.1094 + tsec = tsec, -- seconds since January 1st 1970 00:00 261.1095 + year = year, -- year 261.1096 + month = month, -- month from 1 to 12 261.1097 + day = day, -- day from 1 to 31 261.1098 + hour = hour, -- hour from 0 to 23 261.1099 + minute = minute, -- minute from 0 to 59 261.1100 + second = second -- second from 0 to 59 261.1101 +} 261.1102 + 261.1103 +This method returns a new timestamp value, based on given data. 261.1104 + 261.1105 +--]]-- 261.1106 +function timestamp:new(args) 261.1107 + local args = args 261.1108 + if type(args) == "number" then args = { tsec = args } end 261.1109 + if type(args) == "table" then 261.1110 + if not args.second then 261.1111 + args.second = 0 261.1112 + if not args.minute then 261.1113 + args.minute = 0 261.1114 + if not args.hour then 261.1115 + args.hour = 0 261.1116 + end 261.1117 + end 261.1118 + end 261.1119 + if 261.1120 + type(args.year) == "number" and 261.1121 + type(args.month) == "number" and 261.1122 + type(args.day) == "number" and 261.1123 + type(args.hour) == "number" and 261.1124 + type(args.minute) == "number" and 261.1125 + type(args.second) == "number" 261.1126 + then 261.1127 + if 261.1128 + is_integer(args.year) and 261.1129 + args.year >= 1 and args.year <= 9999 and 261.1130 + is_integer(args.month) and 261.1131 + args.month >= 1 and args.month <= 12 and 261.1132 + is_integer(args.day) and 261.1133 + args.day >= 1 and args.day <= 31 and 261.1134 + is_integer(args.hour) and 261.1135 + args.hour >= 0 and args.hour <= 23 and 261.1136 + is_integer(args.minute) and 261.1137 + args.minute >= 0 and args.minute <= 59 and 261.1138 + is_integer(args.second) and 261.1139 + args.second >= 0 and args.second <= 59 261.1140 + then 261.1141 + return timestamp:_create{ 261.1142 + tsec = timestamp.ymdhms_to_tsec( 261.1143 + args.year, args.month, args.day, 261.1144 + args.hour, args.minute, args.second 261.1145 + ), 261.1146 + year = args.year, 261.1147 + month = args.month, 261.1148 + day = args.day, 261.1149 + hour = args.hour, 261.1150 + minute = args.minute, 261.1151 + second = args.second 261.1152 + } 261.1153 + else 261.1154 + return timestamp.invalid 261.1155 + end 261.1156 + elseif type(args.tsec) == "number" then 261.1157 + if 261.1158 + is_integer(args.tsec) and 261.1159 + args.tsec >= -62135596800 and args.tsec <= 253402300799 261.1160 + then 261.1161 + local year, month, day, hour, minute, second = 261.1162 + timestamp.tsec_to_ymdhms(args.tsec) 261.1163 + return timestamp:_create{ 261.1164 + tsec = args.tsec, 261.1165 + year = year, month = month, day = day, 261.1166 + hour = hour, minute = minute, second = second 261.1167 + } 261.1168 + else 261.1169 + return timestamp.invalid 261.1170 + end 261.1171 + end 261.1172 + end 261.1173 + error("Invalid arguments passed to timestamp constructor.") 261.1174 +end 261.1175 +--//-- 261.1176 + 261.1177 +--[[-- 261.1178 +ts = -- current date/time as timestamp 261.1179 +atom.timestamp:get_current() 261.1180 + 261.1181 +This function returns the current date and time as timestamp. 261.1182 + 261.1183 +--]]-- 261.1184 +function timestamp:get_current() 261.1185 + local now = os.date("*t") 261.1186 + return timestamp{ 261.1187 + year = now.year, month = now.month, day = now.day, 261.1188 + hour = now.hour, minute = now.min, second = now.sec 261.1189 + } 261.1190 +end 261.1191 +--//-- 261.1192 + 261.1193 +--[[-- 261.1194 +ts = -- timestamp represented by the string 261.1195 +atom.timestamp:load( 261.1196 + string -- string representing a timestamp 261.1197 +) 261.1198 + 261.1199 +This method returns a timestamp represented by the given string. 261.1200 + 261.1201 +--]]-- 261.1202 +function timestamp:load(str) 261.1203 + if str == "" then 261.1204 + return nil 261.1205 + else 261.1206 + local year, month, day, hour, minute, second = string.match( 261.1207 + str, 261.1208 + "^([0-9][0-9][0-9][0-9])%-([0-9][0-9])%-([0-9][0-9]) ([0-9][0-9]):([0-9][0-9]):([0-9][0-9])$" 261.1209 + ) 261.1210 + if year then 261.1211 + return timestamp{ 261.1212 + year = tonumber(year), 261.1213 + month = tonumber(month), 261.1214 + day = tonumber(day), 261.1215 + hour = tonumber(hour), 261.1216 + minute = tonumber(minute), 261.1217 + second = tonumber(second) 261.1218 + } 261.1219 + else 261.1220 + return timestamp.invalid 261.1221 + end 261.1222 + end 261.1223 +end 261.1224 + 261.1225 +function timestamp:__tostring() 261.1226 + if self.invalid then 261.1227 + return "invalid_timestamp" 261.1228 + else 261.1229 + return string.format( 261.1230 + "%04i-%02i-%02i %02i:%02i:%02i", 261.1231 + self.year, self.month, self.day, self.hour, self.minute, self.second 261.1232 + ) 261.1233 + end 261.1234 +end 261.1235 + 261.1236 +function timestamp.getters:date() 261.1237 + return date{ year = self.year, month = self.month, day = self.day } 261.1238 +end 261.1239 + 261.1240 +function timestamp.getters:time() 261.1241 + return time{ 261.1242 + hour = self.hour, 261.1243 + minute = self.minute, 261.1244 + second = self.second 261.1245 + } 261.1246 +end 261.1247 + 261.1248 +function timestamp.__eq(value1, value2) 261.1249 + if value1.invalid or value2.invalid then 261.1250 + return false 261.1251 + else 261.1252 + return value1.tsec == value2.tsec 261.1253 + end 261.1254 +end 261.1255 + 261.1256 +function timestamp.__lt(value1, value2) 261.1257 + if value1.invalid or value2.invalid then 261.1258 + return false 261.1259 + else 261.1260 + return value1.tsec < value2.tsec 261.1261 + end 261.1262 +end 261.1263 + 261.1264 +function timestamp.__le(value1, value2) 261.1265 + if value1.invalid or value2.invalid then 261.1266 + return false 261.1267 + else 261.1268 + return value1.tsec <= value2.tsec 261.1269 + end 261.1270 +end 261.1271 + 261.1272 +function timestamp.__add(value1, value2) 261.1273 + if getmetatable(value1) == timestamp then 261.1274 + if getmetatable(value2) == timestamp then 261.1275 + error("Can not add two timestamps.") 261.1276 + elseif type(value2) == "number" then 261.1277 + return timestamp(value1.tsec + value2) 261.1278 + else 261.1279 + error("Right operand of '+' operator has wrong type.") 261.1280 + end 261.1281 + elseif type(value1) == "number" then 261.1282 + if getmetatable(value2) == timestamp then 261.1283 + return timestamp(value1 + value2.tsec) 261.1284 + else 261.1285 + error("Assertion failed") 261.1286 + end 261.1287 + else 261.1288 + error("Left operand of '+' operator has wrong type.") 261.1289 + end 261.1290 +end 261.1291 + 261.1292 +function timestamp.__sub(value1, value2) 261.1293 + if not getmetatable(value1) == timestamp then 261.1294 + error("Left operand of '-' operator has wrong type.") 261.1295 + end 261.1296 + if getmetatable(value2) == timestamp then 261.1297 + return value1.tsec - value2.tsec -- TODO: transform to interval 261.1298 + elseif type(value2) == "number" then 261.1299 + return timestamp(value1.tsec - value2) 261.1300 + else 261.1301 + error("Right operand of '-' operator has wrong type.") 261.1302 + end 261.1303 +end 261.1304 + 261.1305 + 261.1306 + 261.1307 +---------- 261.1308 +-- time -- 261.1309 +---------- 261.1310 + 261.1311 +time = create_new_type("time") 261.1312 + 261.1313 +function time.hms_to_dsec(hour, minute, second) 261.1314 + return 3600 * hour + 60 * minute + second 261.1315 +end 261.1316 + 261.1317 +function time.dsec_to_hms(dsec) 261.1318 + local hour = math.floor(dsec / 3600) 261.1319 + local minute = math.floor((dsec % 3600) / 60) 261.1320 + local second = dsec % 60 261.1321 + return hour, minute, second 261.1322 +end 261.1323 + 261.1324 +--[[-- 261.1325 +atom.time.invalid 261.1326 + 261.1327 +Value representing an invalid time of day. 261.1328 + 261.1329 +--]]-- 261.1330 +time.invalid = time:_create{ 261.1331 + dsec = not_a_number, 261.1332 + hour = not_a_number, minute = not_a_number, second = not_a_number, 261.1333 + invalid = true 261.1334 +} 261.1335 +--//-- 261.1336 + 261.1337 +--[[-- 261.1338 +t = -- time based on given data 261.1339 +atom.time:new{ 261.1340 + dsec = dsec, -- seconds since 00:00:00 261.1341 + hour = hour, -- hour from 0 to 23 261.1342 + minute = minute, -- minute from 0 to 59 261.1343 + second = second -- second from 0 to 59 261.1344 +} 261.1345 + 261.1346 +This method returns a new time value, based on given data. 261.1347 + 261.1348 +--]]-- 261.1349 +function time:new(args) 261.1350 + local args = args 261.1351 + if type(args) == "number" then args = { dsec = args } end 261.1352 + if type(args) == "table" then 261.1353 + if not args.second then 261.1354 + args.second = 0 261.1355 + if not args.minute then 261.1356 + args.minute = 0 261.1357 + end 261.1358 + end 261.1359 + if 261.1360 + type(args.hour) == "number" and 261.1361 + type(args.minute) == "number" and 261.1362 + type(args.second) == "number" 261.1363 + then 261.1364 + if 261.1365 + is_integer(args.hour) and 261.1366 + args.hour >= 0 and args.hour <= 23 and 261.1367 + is_integer(args.minute) and 261.1368 + args.minute >= 0 and args.minute <= 59 and 261.1369 + is_integer(args.second) and 261.1370 + args.second >= 0 and args.second <= 59 261.1371 + then 261.1372 + return time:_create{ 261.1373 + dsec = time.hms_to_dsec(args.hour, args.minute, args.second), 261.1374 + hour = args.hour, 261.1375 + minute = args.minute, 261.1376 + second = args.second 261.1377 + } 261.1378 + else 261.1379 + return time.invalid 261.1380 + end 261.1381 + elseif type(args.dsec) == "number" then 261.1382 + if 261.1383 + is_integer(args.dsec) and 261.1384 + args.dsec >= 0 and args.dsec <= 86399 261.1385 + then 261.1386 + local hour, minute, second = 261.1387 + time.dsec_to_hms(args.dsec) 261.1388 + return time:_create{ 261.1389 + dsec = args.dsec, 261.1390 + hour = hour, minute = minute, second = second 261.1391 + } 261.1392 + else 261.1393 + return time.invalid 261.1394 + end 261.1395 + end 261.1396 + end 261.1397 + error("Invalid arguments passed to time constructor.") 261.1398 +end 261.1399 +--//-- 261.1400 + 261.1401 +--[[-- 261.1402 +t = -- current time of day 261.1403 +atom.time:get_current() 261.1404 + 261.1405 +This method returns the current time of the day. 261.1406 + 261.1407 +--]]-- 261.1408 +function time:get_current() 261.1409 + local now = os.date("*t") 261.1410 + return time{ hour = now.hour, minute = now.min, second = now.sec } 261.1411 +end 261.1412 +--//-- 261.1413 + 261.1414 +--[[-- 261.1415 +t = -- time represented by the string 261.1416 +atom.time:load( 261.1417 + string -- string representing a time of day 261.1418 +) 261.1419 + 261.1420 +This method returns a time represented by the given string. 261.1421 + 261.1422 +--]]-- 261.1423 +function time:load(str) 261.1424 + if str == "" then 261.1425 + return nil 261.1426 + else 261.1427 + local hour, minute, second = string.match( 261.1428 + str, 261.1429 + "^([0-9][0-9]):([0-9][0-9]):([0-9][0-9])$" 261.1430 + ) 261.1431 + if hour then 261.1432 + return time{ 261.1433 + hour = tonumber(hour), 261.1434 + minute = tonumber(minute), 261.1435 + second = tonumber(second) 261.1436 + } 261.1437 + else 261.1438 + return time.invalid 261.1439 + end 261.1440 + end 261.1441 +end 261.1442 +--//-- 261.1443 + 261.1444 +function time:__tostring() 261.1445 + if self.invalid then 261.1446 + return "invalid_time" 261.1447 + else 261.1448 + return string.format( 261.1449 + "%02i:%02i:%02i", 261.1450 + self.hour, self.minute, self.second 261.1451 + ) 261.1452 + end 261.1453 +end 261.1454 + 261.1455 +function time.__eq(value1, value2) 261.1456 + if value1.invalid or value2.invalid then 261.1457 + return false 261.1458 + else 261.1459 + return value1.dsec == value2.dsec 261.1460 + end 261.1461 +end 261.1462 + 261.1463 +function time.__lt(value1, value2) 261.1464 + if value1.invalid or value2.invalid then 261.1465 + return false 261.1466 + else 261.1467 + return value1.dsec < value2.dsec 261.1468 + end 261.1469 +end 261.1470 + 261.1471 +function time.__le(value1, value2) 261.1472 + if value1.invalid or value2.invalid then 261.1473 + return false 261.1474 + else 261.1475 + return value1.dsec <= value2.dsec 261.1476 + end 261.1477 +end 261.1478 + 261.1479 +function time.__add(value1, value2) 261.1480 + if getmetatable(value1) == time then 261.1481 + if getmetatable(value2) == time then 261.1482 + error("Can not add two times.") 261.1483 + elseif type(value2) == "number" then 261.1484 + return time((value1.dsec + value2) % 86400) 261.1485 + else 261.1486 + error("Right operand of '+' operator has wrong type.") 261.1487 + end 261.1488 + elseif type(value1) == "number" then 261.1489 + if getmetatable(value2) == time then 261.1490 + return time((value1 + value2.dsec) % 86400) 261.1491 + else 261.1492 + error("Assertion failed") 261.1493 + end 261.1494 + else 261.1495 + error("Left operand of '+' operator has wrong type.") 261.1496 + end 261.1497 +end 261.1498 + 261.1499 +function time.__sub(value1, value2) 261.1500 + if not getmetatable(value1) == time then 261.1501 + error("Left operand of '-' operator has wrong type.") 261.1502 + end 261.1503 + if getmetatable(value2) == time then 261.1504 + return value1.dsec - value2.dsec -- TODO: transform to interval 261.1505 + elseif type(value2) == "number" then 261.1506 + return time((value1.dsec - value2) % 86400) 261.1507 + else 261.1508 + error("Right operand of '-' operator has wrong type.") 261.1509 + end 261.1510 +end 261.1511 + 261.1512 +function time.__concat(value1, value2) 261.1513 + local mt1, mt2 = getmetatable(value1), getmetatable(value2) 261.1514 + if mt1 == date and mt2 == time then 261.1515 + return timestamp{ 261.1516 + year = value1.year, month = value1.month, day = value1.day, 261.1517 + hour = value2.hour, minute = value2.minute, second = value2.second 261.1518 + } 261.1519 + elseif mt1 == time and mt2 == date then 261.1520 + return timestamp{ 261.1521 + year = value2.year, month = value2.month, day = value2.day, 261.1522 + hour = value1.hour, minute = value1.minute, second = value1.second 261.1523 + } 261.1524 + elseif mt1 == time then 261.1525 + error("Right operand of '..' operator has wrong type.") 261.1526 + elseif mt2 == time then 261.1527 + error("Left operand of '..' operator has wrong type.") 261.1528 + else 261.1529 + error("Assertion failed") 261.1530 + end 261.1531 +end 261.1532 +
262.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 262.2 +++ b/libraries/atom/benchmark.sh Sun Oct 25 12:00:00 2009 +0100 262.3 @@ -0,0 +1,12 @@ 262.4 +#!/bin/ksh 262.5 +luac -o atom-compiled.lua atom.lua 262.6 +luac -s -o atom-stripped.lua atom.lua 262.7 +echo "100x loading source:" 262.8 +time for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100; do lua -l atom -e ''; done 262.9 +echo "100x loading precompiled bytecode:" 262.10 +time for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100; do lua -l atom-compiled -e ''; done 262.11 +echo "100x loading stripped bytecode:" 262.12 +time for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100; do lua -l atom-stripped -e ''; done 262.13 +echo "100x loading nothing:" 262.14 +time for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100; do lua -e ''; done 262.15 +rm atom-compiled.lua atom-stripped.lua
263.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 263.2 +++ b/libraries/extos/Makefile Sun Oct 25 12:00:00 2009 +0100 263.3 @@ -0,0 +1,10 @@ 263.4 +include ../../Makefile.options 263.5 + 263.6 +extos.so: extos.o 263.7 + $(LD) $(LDFLAGS) -lrt -lcrypt -o extos.$(SLIB_EXT) extos.o 263.8 + 263.9 +extos.o: extos.c 263.10 + $(CC) -c $(CFLAGS) -o extos.o extos.c 263.11 + 263.12 +clean:: 263.13 + rm -f extos.so extos.o
264.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 264.2 +++ b/libraries/extos/extos.c Sun Oct 25 12:00:00 2009 +0100 264.3 @@ -0,0 +1,351 @@ 264.4 +#include <lua.h> 264.5 +#include <lauxlib.h> 264.6 +#include <dirent.h> 264.7 +#include <time.h> 264.8 +#include <unistd.h> 264.9 +#include <sys/types.h> 264.10 +#include <sys/wait.h> 264.11 +#include <signal.h> 264.12 +#include <errno.h> 264.13 +#include <stdio.h> 264.14 +#include <string.h> 264.15 +#include <fcntl.h> 264.16 +#include <poll.h> 264.17 +#include <stdlib.h> 264.18 + 264.19 +#define EXTOS_MAX_ERRLEN 80 264.20 +#define EXTOS_EXEC_MAX_ARGS 64 264.21 + 264.22 +static lua_Number extos_monotonic_start_time; 264.23 + 264.24 +static int extos_pfilter(lua_State *L) { 264.25 + int i, result, exit_status, status_pipe_len; 264.26 + const char *in_buf; 264.27 + size_t in_len; 264.28 + size_t in_pos = 0; 264.29 + const char *filename; 264.30 + const char *args[EXTOS_EXEC_MAX_ARGS+2]; 264.31 + int pipe_status[2]; 264.32 + int pipe_in[2]; 264.33 + int pipe_out[2]; 264.34 + int pipe_err[2]; 264.35 + pid_t child; 264.36 + char status_buf[1]; 264.37 + char *out_buf = NULL; 264.38 + size_t out_len = 1024; 264.39 + size_t out_pos = 0; 264.40 + char *err_buf = NULL; 264.41 + size_t err_len = 1024; 264.42 + size_t err_pos = 0; 264.43 + void *old_sigpipe_action; 264.44 + struct pollfd fds[3]; 264.45 + int in_closed = 0; 264.46 + int out_closed = 0; 264.47 + int err_closed = 0; 264.48 + void *newptr; 264.49 + char errmsg[EXTOS_MAX_ERRLEN+1]; 264.50 + in_buf = luaL_optlstring(L, 1, "", &in_len); 264.51 + filename = luaL_checkstring(L, 2); 264.52 + args[0] = filename; 264.53 + for (i = 0; i < EXTOS_EXEC_MAX_ARGS; i++) { 264.54 + if (lua_isnoneornil(L, 3+i)) break; 264.55 + else args[i+1] = luaL_checkstring(L, 3+i); 264.56 + } 264.57 + if (!lua_isnoneornil(L, 3+i)) { 264.58 + return luaL_error(L, "Too many arguments for pfilter call."); 264.59 + } 264.60 + args[i+1] = 0; 264.61 + // status pipe for internal communication 264.62 + if (pipe(pipe_status) < 0) { 264.63 + strerror_r(errno, errmsg, EXTOS_MAX_ERRLEN+1); 264.64 + goto extos_pfilter_error_A0; 264.65 + } 264.66 + // stdin 264.67 + if (pipe(pipe_in) < 0) { 264.68 + strerror_r(errno, errmsg, EXTOS_MAX_ERRLEN+1); 264.69 + goto extos_pfilter_error_A1; 264.70 + } 264.71 + if (in_len) { 264.72 + do result = fcntl(pipe_in[1], F_SETFL, O_NONBLOCK); while (result < 0 && errno == EINTR); 264.73 + } else { 264.74 + do result = close(pipe_in[1]); while (result < 0 && errno == EINTR); 264.75 + in_closed = 1; 264.76 + } 264.77 + if (result < 0) { 264.78 + strerror_r(errno, errmsg, EXTOS_MAX_ERRLEN+1); 264.79 + goto extos_pfilter_error_A2; 264.80 + } 264.81 + // stdout 264.82 + if (pipe(pipe_out) < 0) { 264.83 + strerror_r(errno, errmsg, EXTOS_MAX_ERRLEN+1); 264.84 + goto extos_pfilter_error_A2; 264.85 + } 264.86 + do result = fcntl(pipe_out[0], F_SETFL, O_NONBLOCK); while (result < 0 && errno == EINTR); 264.87 + if (result < 0) { 264.88 + strerror_r(errno, errmsg, EXTOS_MAX_ERRLEN+1); 264.89 + goto extos_pfilter_error_A3; 264.90 + } 264.91 + // stderr 264.92 + if (pipe(pipe_err) < 0) { 264.93 + strerror_r(errno, errmsg, EXTOS_MAX_ERRLEN+1); 264.94 + goto extos_pfilter_error_A3; 264.95 + } 264.96 + do result = fcntl(pipe_err[0], F_SETFL, O_NONBLOCK); while (result < 0 && errno == EINTR); 264.97 + if (result < 0) { 264.98 + strerror_r(errno, errmsg, EXTOS_MAX_ERRLEN+1); 264.99 + goto extos_pfilter_error_A4; 264.100 + } 264.101 + // fork 264.102 + child = fork(); 264.103 + if (child < 0) { 264.104 + strerror_r(errno, errmsg, EXTOS_MAX_ERRLEN+1); 264.105 + goto extos_pfilter_error_A4; 264.106 + } 264.107 + // skip error handling 264.108 + goto extos_pfilter_success_A; 264.109 + // error handling 264.110 + extos_pfilter_error_A4: 264.111 + do result = close(pipe_err[0]); while (result < 0 && errno == EINTR); 264.112 + do result = close(pipe_err[1]); while (result < 0 && errno == EINTR); 264.113 + extos_pfilter_error_A3: 264.114 + do result = close(pipe_out[0]); while (result < 0 && errno == EINTR); 264.115 + do result = close(pipe_out[1]); while (result < 0 && errno == EINTR); 264.116 + extos_pfilter_error_A2: 264.117 + do result = close(pipe_in[0]); while (result < 0 && errno == EINTR); 264.118 + do result = close(pipe_in[1]); while (result < 0 && errno == EINTR); 264.119 + extos_pfilter_error_A1: 264.120 + do result = close(pipe_status[0]); while (result < 0 && errno == EINTR); 264.121 + do result = close(pipe_status[1]); while (result < 0 && errno == EINTR); 264.122 + extos_pfilter_error_A0: 264.123 + return luaL_error(L, "Unexpected error in pfilter: %s", errmsg); 264.124 + // end of error handling 264.125 + extos_pfilter_success_A: 264.126 + if (child) { // parent 264.127 + old_sigpipe_action = signal(SIGPIPE, SIG_IGN); 264.128 + do result = close(pipe_status[1]); while (result < 0 && errno == EINTR); 264.129 + if (result < 0) goto extos_pfilter_error_B; 264.130 + do result = close(pipe_in[0]); while (result < 0 && errno == EINTR); 264.131 + if (result < 0) goto extos_pfilter_error_B; 264.132 + do result = close(pipe_out[1]); while (result < 0 && errno == EINTR); 264.133 + if (result < 0) goto extos_pfilter_error_B; 264.134 + do result = close(pipe_err[1]); while (result < 0 && errno == EINTR); 264.135 + if (result < 0) goto extos_pfilter_error_B; 264.136 + out_buf = malloc(out_len * sizeof(char)); 264.137 + if (!out_buf) goto extos_pfilter_error_B; 264.138 + err_buf = malloc(err_len * sizeof(char)); 264.139 + if (!err_buf) goto extos_pfilter_error_B; 264.140 + while (!in_closed || !out_closed || !err_closed) { 264.141 + i = 0; 264.142 + if (!in_closed) { 264.143 + fds[i].fd = pipe_in[1]; 264.144 + fds[i].events = POLLOUT; 264.145 + i++; 264.146 + } 264.147 + if (!out_closed) { 264.148 + fds[i].fd = pipe_out[0]; 264.149 + fds[i].events = POLLIN; 264.150 + i++; 264.151 + } 264.152 + if (!err_closed) { 264.153 + fds[i].fd = pipe_err[0]; 264.154 + fds[i].events = POLLIN; 264.155 + i++; 264.156 + } 264.157 + do result = poll(fds, i, -1); while (result < 0 && errno == EINTR); 264.158 + if (result < 0) goto extos_pfilter_error_B; 264.159 + if (!in_closed) { 264.160 + do result = write(pipe_in[1], in_buf+in_pos, in_len-in_pos); while (result < 0 && errno == EINTR); 264.161 + if (result < 0) { 264.162 + if (errno == EPIPE) { 264.163 + do result = close(pipe_in[1]); while (result < 0 && errno == EINTR); 264.164 + in_closed = 1; 264.165 + } else if (errno != EAGAIN) { 264.166 + goto extos_pfilter_error_B; 264.167 + } 264.168 + } else { 264.169 + in_pos += result; 264.170 + if (in_pos == in_len) { 264.171 + do result = close(pipe_in[1]); while (result < 0 && errno == EINTR); 264.172 + in_closed = 1; 264.173 + } 264.174 + } 264.175 + } 264.176 + if (!out_closed) { 264.177 + do result = read(pipe_out[0], out_buf+out_pos, out_len-out_pos); while (result < 0 && errno == EINTR); 264.178 + if (result < 0) { 264.179 + if (errno != EAGAIN) goto extos_pfilter_error_B; 264.180 + } else if (result == 0) { 264.181 + do result = close(pipe_out[0]); while (result < 0 && errno == EINTR); 264.182 + out_closed = 1; 264.183 + } else { 264.184 + out_pos += result; 264.185 + if (out_pos == out_len) { 264.186 + out_len *= 2; 264.187 + newptr = realloc(out_buf, out_len * sizeof(char)); 264.188 + if (!newptr) goto extos_pfilter_error_B; 264.189 + out_buf = newptr; 264.190 + } 264.191 + } 264.192 + } 264.193 + if (!err_closed) { 264.194 + do result = read(pipe_err[0], err_buf+err_pos, err_len-err_pos); while (result < 0 && errno == EINTR); 264.195 + if (result < 0) { 264.196 + if (errno != EAGAIN) goto extos_pfilter_error_B; 264.197 + } else if (result == 0) { 264.198 + do result = close(pipe_err[0]); while (result < 0 && errno == EINTR); 264.199 + err_closed = 1; 264.200 + } else { 264.201 + err_pos += result; 264.202 + if (err_pos == err_len) { 264.203 + err_len *= 2; 264.204 + newptr = realloc(err_buf, err_len * sizeof(char)); 264.205 + if (!newptr) goto extos_pfilter_error_B; 264.206 + err_buf = newptr; 264.207 + } 264.208 + } 264.209 + } 264.210 + } 264.211 + lua_pushlstring(L, out_buf, out_pos); 264.212 + free(out_buf); 264.213 + out_buf = NULL; 264.214 + lua_pushlstring(L, err_buf, err_pos); 264.215 + free(err_buf); 264.216 + err_buf = NULL; 264.217 + do result = waitpid(child, &exit_status, 0); while (result < 0 && errno == EINTR); 264.218 + child = 0; 264.219 + if (result < 0) goto extos_pfilter_error_B; 264.220 + do status_pipe_len = read(pipe_status[0], status_buf, 1); while (status_pipe_len < 0 && errno == EINTR); 264.221 + if (status_pipe_len < 0) goto extos_pfilter_error_B; 264.222 + do result = close(pipe_status[0]); while (result < 0 && errno == EINTR); 264.223 + signal(SIGPIPE, old_sigpipe_action); 264.224 + if (status_pipe_len == 0) { 264.225 + if (WIFEXITED(exit_status)) lua_pushinteger(L, WEXITSTATUS(exit_status)); 264.226 + else lua_pushinteger(L, -WTERMSIG(exit_status)); 264.227 + return 3; 264.228 + } else if (status_buf[0] == 0) { 264.229 + return luaL_error(L, "Error in pfilter while reopening standard file descriptors in child process."); 264.230 + } else { 264.231 + strerror_r(status_buf[0], errmsg, EXTOS_MAX_ERRLEN+1); 264.232 + lua_pushnil(L); 264.233 + lua_pushfstring(L, "Could not execute \"%s\": %s", filename, errmsg); 264.234 + return 2; 264.235 + } 264.236 + extos_pfilter_error_B: 264.237 + signal(SIGPIPE, old_sigpipe_action); 264.238 + strerror_r(errno, errmsg, EXTOS_MAX_ERRLEN+1); 264.239 + if (out_buf) free(out_buf); 264.240 + if (err_buf) free(err_buf); 264.241 + if (!in_closed) { 264.242 + do result = close(pipe_in[1]); while (result < 0 && errno == EINTR); 264.243 + } 264.244 + if (!out_closed) { 264.245 + do result = close(pipe_out[0]); while (result < 0 && errno == EINTR); 264.246 + } 264.247 + if (!err_closed) { 264.248 + do result = close(pipe_err[0]); while (result < 0 && errno == EINTR); 264.249 + } 264.250 + if (child) do result = waitpid(child, &exit_status, 0); while (result < 0 && errno == EINTR); 264.251 + return luaL_error(L, "Unexpected error in pfilter: %s", errmsg); 264.252 + } else { // child 264.253 + do result = close(pipe_status[0]); while (result < 0 && errno == EINTR); 264.254 + do result = close(pipe_in[1]); while (result < 0 && errno == EINTR); 264.255 + do result = close(pipe_out[0]); while (result < 0 && errno == EINTR); 264.256 + do result = close(0); while (result < 0 && errno == EINTR); 264.257 + do result = close(1); while (result < 0 && errno == EINTR); 264.258 + do result = close(2); while (result < 0 && errno == EINTR); 264.259 + do result = dup(pipe_in[0]); while (result < 0 && errno == EINTR); 264.260 + if (result != 0) goto extos_pfilter_error_fd_remapping; 264.261 + do result = dup(pipe_out[1]); while (result < 0 && errno == EINTR); 264.262 + if (result != 1) goto extos_pfilter_error_fd_remapping; 264.263 + do result = dup(pipe_err[1]); while (result < 0 && errno == EINTR); 264.264 + if (result != 2) goto extos_pfilter_error_fd_remapping; 264.265 + execvp(filename, args); 264.266 + status_buf[0] = errno; 264.267 + do result = write(pipe_status[1], status_buf, 1); while (result < 0 && errno == EINTR); 264.268 + _exit(0); 264.269 + extos_pfilter_error_fd_remapping: 264.270 + status_buf[0] = 0; 264.271 + do result = write(pipe_status[1], status_buf, 1); while (result < 0 && errno == EINTR); 264.272 + _exit(0); 264.273 + } 264.274 +} 264.275 + 264.276 +static int extos_listdir(lua_State *L) { 264.277 + DIR *dir; 264.278 + int i = 1; 264.279 + struct dirent entry_buffer; 264.280 + struct dirent *entry; 264.281 + dir = opendir(luaL_checkstring(L, 1)); 264.282 + if (!dir) { 264.283 + lua_pushnil(L); 264.284 + lua_pushliteral(L, "Could not list directory."); 264.285 + return 2; 264.286 + } 264.287 + lua_settop(L, 0); 264.288 + lua_newtable(L); // 1 264.289 + while (1) { 264.290 + readdir_r(dir, &entry_buffer, &entry); 264.291 + if (!entry) break; 264.292 + // Linux doesn't have d_namlen 264.293 + //lua_pushlstring(L, entry->d_name, entry->d_namlen); 264.294 + lua_pushstring(L, entry->d_name); 264.295 + lua_rawseti(L, 1, i++); 264.296 + } 264.297 + closedir(dir); 264.298 + return 1; 264.299 +} 264.300 + 264.301 +static int extos_crypt(lua_State *L) { 264.302 + char *key; 264.303 + char *salt; 264.304 + char *result; 264.305 + key = luaL_checkstring(L, 1); 264.306 + salt = luaL_checkstring(L, 2); 264.307 + result = crypt(key, salt); // TODO: Call not thread safe 264.308 + if (result) lua_pushstring(L, result); 264.309 + else lua_pushnil(L); 264.310 + return 1; 264.311 +} 264.312 + 264.313 +static int extos_hires_time(lua_State *L) { 264.314 + struct timespec tp; 264.315 + if (clock_gettime(CLOCK_REALTIME, &tp)) { 264.316 + return luaL_error(L, "Could not access CLOCK_REALTIME."); 264.317 + } 264.318 + lua_pushnumber(L, tp.tv_sec + 0.000000001 * tp.tv_nsec); 264.319 + return 1; 264.320 +} 264.321 + 264.322 +// returns time in seconds since loading the library 264.323 +static int extos_monotonic_hires_time(lua_State *L) { 264.324 + struct timespec tp; 264.325 + if (clock_gettime(CLOCK_MONOTONIC, &tp)) { 264.326 + return luaL_error(L, "Could not access CLOCK_MONOTONIC."); 264.327 + } 264.328 + lua_pushnumber(L, 264.329 + tp.tv_sec + 0.000000001 * tp.tv_nsec - extos_monotonic_start_time 264.330 + ); 264.331 + return 1; 264.332 +} 264.333 + 264.334 +int luaopen_extos(lua_State *L) { 264.335 + { 264.336 + struct timespec tp; 264.337 + if (clock_gettime(CLOCK_MONOTONIC, &tp)) { 264.338 + return luaL_error(L, "Could not access monotonic hires time."); 264.339 + } 264.340 + extos_monotonic_start_time = tp.tv_sec + 0.000000001 * tp.tv_nsec; 264.341 + } 264.342 + lua_getglobal(L, "os"); 264.343 + lua_pushcfunction(L, extos_pfilter); 264.344 + lua_setfield(L, -2, "pfilter"); 264.345 + lua_pushcfunction(L, extos_listdir); 264.346 + lua_setfield(L, -2, "listdir"); 264.347 + lua_pushcfunction(L, extos_crypt); 264.348 + lua_setfield(L, -2, "crypt"); 264.349 + lua_pushcfunction(L, extos_hires_time); 264.350 + lua_setfield(L, -2, "hires_time"); 264.351 + lua_pushcfunction(L, extos_monotonic_hires_time); 264.352 + lua_setfield(L, -2, "monotonic_hires_time"); 264.353 + return 0; 264.354 +}
265.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 265.2 +++ b/libraries/luatex/luatex.lua Sun Oct 25 12:00:00 2009 +0100 265.3 @@ -0,0 +1,148 @@ 265.4 +#!/usr/bin/env lua 265.5 + 265.6 +local _G = _G 265.7 +local _VERSION = _VERSION 265.8 +local assert = assert 265.9 +local collectgarbage = collectgarbage 265.10 +local dofile = dofile 265.11 +local error = error 265.12 +local getfenv = getfenv 265.13 +local getmetatable = getmetatable 265.14 +local ipairs = ipairs 265.15 +local load = load 265.16 +local loadfile = loadfile 265.17 +local loadstring = loadstring 265.18 +local module = module 265.19 +local next = next 265.20 +local pairs = pairs 265.21 +local pcall = pcall 265.22 +local print = print 265.23 +local rawequal = rawequal 265.24 +local rawget = rawget 265.25 +local rawset = rawset 265.26 +local require = require 265.27 +local select = select 265.28 +local setfenv = setfenv 265.29 +local setmetatable = setmetatable 265.30 +local tonumber = tonumber 265.31 +local tostring = tostring 265.32 +local type = type 265.33 +local unpack = unpack 265.34 +local xpcall = xpcall 265.35 + 265.36 +local coroutine = coroutine 265.37 +local debug = debug 265.38 +local io = io 265.39 +local math = math 265.40 +local os = os 265.41 +local package = package 265.42 +local string = string 265.43 +local table = table 265.44 + 265.45 +require("multirand") 265.46 +local multirand = multirand 265.47 + 265.48 +module(...) 265.49 + 265.50 +temp_dir = false -- has to be set to a private directory (/tmp can be unsafe) 265.51 + 265.52 +function escape(str) 265.53 + return ( 265.54 + string.gsub( 265.55 + str, 265.56 + "[\001-\031\127\\#$&~_^%%{}]", 265.57 + function(char) 265.58 + local b = string.byte(char) 265.59 + if (b > 1 and b < 31) or b == 127 then 265.60 + return " " 265.61 + elseif 265.62 + char == "#" or char == "$" or char == "&" or char == "_" or 265.63 + char == "%" or char == "{" or char == "}" 265.64 + then 265.65 + return "\\" .. char 265.66 + else 265.67 + return "\\symbol{" .. b .. "}" 265.68 + end 265.69 + end 265.70 + ) 265.71 + ) 265.72 +end 265.73 + 265.74 +document_methods = {} 265.75 + 265.76 +document_mt = { 265.77 + __index = document_methods, 265.78 + __call = function(...) return document_methods.write(...) end 265.79 +} 265.80 + 265.81 +function new_document() 265.82 + return setmetatable({}, document_mt) 265.83 +end 265.84 + 265.85 +function document_methods:write(...) 265.86 + local i = 1 265.87 + while true do 265.88 + local v = select(i, ...) 265.89 + if v == nil then 265.90 + break 265.91 + end 265.92 + self[#self+1] = v 265.93 + i = i + 1 265.94 + end 265.95 +end 265.96 + 265.97 +function document_methods:get_latex() 265.98 + local str = table.concat(self) 265.99 + for i in ipairs(self) do 265.100 + self[i] = nil 265.101 + end 265.102 + self[1] = str 265.103 + return str 265.104 +end 265.105 + 265.106 +function document_methods:get_pdf() 265.107 + -- TODO: proper escaping of shell commands (should not be a real risk) 265.108 + if not temp_dir then 265.109 + error("luatex.temp_dir not set") 265.110 + end 265.111 + local basename = temp_dir .. "/tmp.luatex_" .. multirand.string(16) 265.112 + local latex_file = assert(io.open(basename .. ".tex", "w")) 265.113 + latex_file:write(self:get_latex()) 265.114 + latex_file:close() 265.115 + local result = os.execute( 265.116 + 'latex -output-format=pdf "-output-directory=' .. temp_dir .. '" ' .. 265.117 + basename .. '< /dev/null > /dev/null 2> /dev/null' 265.118 + ) 265.119 + if result ~= 0 then 265.120 + error('LaTeX failed, see "' .. basename .. '.log" for details.') 265.121 + end 265.122 + local pdf_file = assert(io.open(basename .. ".pdf", "r")) 265.123 + local pdf_data = pdf_file:read("*a") 265.124 + pdf_file:close() 265.125 + os.execute('rm -f "' .. basename .. '.*"') 265.126 + return pdf_data 265.127 +end 265.128 + 265.129 +--[[ 265.130 + 265.131 +require("luatex") 265.132 +luatex.temp_dir = "." 265.133 + 265.134 +local tex = luatex.new_document() 265.135 + 265.136 +tex "\\documentclass[a4paper,12pt]{article}\n" 265.137 +tex "\\usepackage{german}\n" 265.138 +tex "\\usepackage{amsfonts}\n" 265.139 +tex "\\usepackage{amssymb}\n" 265.140 +tex "\\usepackage{ulem}\n" 265.141 +tex "\\pagestyle{headings}\n" 265.142 +tex "\\begin{document}\n" 265.143 +tex "\\title{Demo}\n" 265.144 +tex "\\author{John Doe}\n" 265.145 +tex "\\date{\\small 25. August 2008}\n" 265.146 +tex "\\maketitle\n" 265.147 +tex "\\end{document}\n" 265.148 + 265.149 +local pdf = tex:get_pdf() 265.150 + 265.151 +--]]
266.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 266.2 +++ b/libraries/mondelefant/Makefile Sun Oct 25 12:00:00 2009 +0100 266.3 @@ -0,0 +1,13 @@ 266.4 +include ../../Makefile.options 266.5 + 266.6 +mondelefant_native.so: mondelefant_native.o 266.7 + $(LD) $(LDFLAGS) $(LDFLAGS_PGSQL) -o mondelefant_native.$(SLIB_EXT) mondelefant_native.o -lpq 266.8 + 266.9 +mondelefant_native.o: mondelefant_native.c 266.10 + $(CC) -c $(CFLAGS) $(CFLAGS_PGSQL) -o mondelefant_native.o mondelefant_native.c 266.11 + 266.12 +test:: mondelefant_native.so mondelefant.lua 266.13 + lua -l mondelefant 266.14 + 266.15 +clean:: 266.16 + rm -f mondelefant_native.so mondelefant_native.o
267.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 267.2 +++ b/libraries/mondelefant/example.lua Sun Oct 25 12:00:00 2009 +0100 267.3 @@ -0,0 +1,86 @@ 267.4 +#!/usr/bin/env lua 267.5 +require("mondelefant") 267.6 + 267.7 +-- Standarddatenbankverbindung ist globale Variable 'db' 267.8 +function mondelefant.class_prototype:get_db_conn() return db end 267.9 + 267.10 +-- Verbindung aufbauen 267.11 +db = assert(mondelefant.connect{engine='postgresql', dbname='test'}) 267.12 + 267.13 +Product = mondelefant.new_class{ table = "product" } 267.14 +ProductVariant = mondelefant.new_class{ table = "product_variant" } 267.15 + 267.16 +Product:add_reference{ 267.17 + mode = "1m", 267.18 + to = ProductVariant, 267.19 + this_key = "number", 267.20 + that_key = "product_number", 267.21 + ref = "product_variants", 267.22 + back_ref = "product", 267.23 + --default_order = '"number"' 267.24 +} 267.25 + 267.26 +ProductVariant:add_reference{ 267.27 + mode = "m1", 267.28 + to = Product, 267.29 + this_key = "product_number", 267.30 + that_key = "number", 267.31 + ref = "product", 267.32 + back_ref = nil, 267.33 + --default_order = '"id"' 267.34 +} 267.35 + 267.36 +p = Product:new_selector():single_object_mode():add_where{"name=?", "Noodles"}:exec() 267.37 + 267.38 + 267.39 +--[[ 267.40 +-- Neue Datenbankklasse definieren 267.41 +Product = mondelefant.new_class{ table = '"product"' } 267.42 + 267.43 +-- Methode der Klasse, um sofort eine alphabetische Liste aller Produkte 267.44 +-- zu bekommen 267.45 +function Product:get_all_ordered_by_name() 267.46 + local selector = self:new_selector() 267.47 + selector:add_order_by('"name"') 267.48 + selector:add_order_by('"id"') 267.49 + return selector:exec() 267.50 +end 267.51 + 267.52 +function Product.object_get:name_length(key) 267.53 + local value = #self.name 267.54 + self._data.name_length = value 267.55 + return value 267.56 +end 267.57 + 267.58 +function Product.object_set:quality(value) 267.59 + if value == "perfect" or value == "good" or value == "trash" then 267.60 + self._data.quality = value 267.61 + else 267.62 + self._data.quality = nil 267.63 + end 267.64 + self._dirty.quality = true 267.65 +end 267.66 + 267.67 +-- Methode der Listen, um sie auszugeben 267.68 +function Product.list:print() 267.69 + for i, product in ipairs(self) do 267.70 + print(product.id, product.name, product.name_length) 267.71 + end 267.72 +end 267.73 + 267.74 +products = Product:get_all_ordered_by_name() 267.75 +products:print() 267.76 +products[1].quality = "perfect" 267.77 +print(products[1].quality) 267.78 +products[2].quality = "I don't know." 267.79 +print(products[2].quality) 267.80 +products[3].name = "Produkt Eins!" 267.81 +products[3].quality = "perfect" 267.82 +products[3]:save() 267.83 + 267.84 +sel = db:new_selector() 267.85 +sel:from('"product_variant"') 267.86 +sel:add_field("*") 267.87 +sel:attach("m1", products, "product_id", "id", "product", "product_variants") 267.88 +product_variants = sel:exec() 267.89 +--]] 267.90 \ No newline at end of file
268.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 268.2 +++ b/libraries/mondelefant/mondelefant.lua Sun Oct 25 12:00:00 2009 +0100 268.3 @@ -0,0 +1,845 @@ 268.4 +#!/usr/bin/env lua 268.5 + 268.6 + 268.7 +--------------------------- 268.8 +-- module initialization -- 268.9 +--------------------------- 268.10 + 268.11 +local _G = _G 268.12 +local _VERSION = _VERSION 268.13 +local assert = assert 268.14 +local collectgarbage = collectgarbage 268.15 +local dofile = dofile 268.16 +local error = error 268.17 +local getfenv = getfenv 268.18 +local getmetatable = getmetatable 268.19 +local ipairs = ipairs 268.20 +local load = load 268.21 +local loadfile = loadfile 268.22 +local loadstring = loadstring 268.23 +local next = next 268.24 +local pairs = pairs 268.25 +local pcall = pcall 268.26 +local print = print 268.27 +local rawequal = rawequal 268.28 +local rawget = rawget 268.29 +local rawset = rawset 268.30 +local select = select 268.31 +local setfenv = setfenv 268.32 +local setmetatable = setmetatable 268.33 +local tonumber = tonumber 268.34 +local tostring = tostring 268.35 +local type = type 268.36 +local unpack = unpack 268.37 +local xpcall = xpcall 268.38 + 268.39 +local coroutine = coroutine 268.40 +local io = io 268.41 +local math = math 268.42 +local os = os 268.43 +local string = string 268.44 +local table = table 268.45 + 268.46 +local add = table.insert 268.47 + 268.48 +_G[...] = require("mondelefant_native") 268.49 +module(...) 268.50 + 268.51 + 268.52 + 268.53 +--------------- 268.54 +-- selectors -- 268.55 +--------------- 268.56 + 268.57 +selector_metatable = {} 268.58 +selector_prototype = {} 268.59 +selector_metatable.__index = selector_prototype 268.60 + 268.61 +local function init_selector(self, db_conn) 268.62 + self._db_conn = db_conn 268.63 + self._mode = "list" 268.64 + self._fields = { sep = ", " } 268.65 + self._distinct = false 268.66 + self._distinct_on = {sep = ", ", expression} 268.67 + self._from = { sep = " " } 268.68 + self._where = { sep = " AND " } 268.69 + self._group_by = { sep = ", " } 268.70 + self._having = { sep = " AND " } 268.71 + self._combine = { sep = " " } 268.72 + self._order_by = { sep = ", " } 268.73 + self._limit = nil 268.74 + self._offset = nil 268.75 + --[[ 268.76 + self._lock = nil 268.77 + self._lock_tables = { sep = ", " } 268.78 + --]] 268.79 + self._class = nil 268.80 + self._attach = nil 268.81 + return self 268.82 +end 268.83 + 268.84 +function connection_prototype:new_selector() 268.85 + return init_selector(setmetatable({}, selector_metatable), self) 268.86 +end 268.87 + 268.88 +function selector_prototype:get_db_conn() 268.89 + return self._db_conn 268.90 +end 268.91 + 268.92 +-- TODO: selector clone? 268.93 + 268.94 +function selector_prototype:single_object_mode() 268.95 + self._mode = "object" 268.96 + return self 268.97 +end 268.98 + 268.99 +function selector_prototype:optional_object_mode() 268.100 + self._mode = "opt_object" 268.101 + return self 268.102 +end 268.103 + 268.104 +function selector_prototype:empty_list_mode() 268.105 + self._mode = "empty_list" 268.106 + return self 268.107 +end 268.108 + 268.109 +function selector_prototype:add_distinct_on(expression) 268.110 + if self._distinct then 268.111 + error("Can not combine DISTINCT with DISTINCT ON.") 268.112 + end 268.113 + add(self._distinct_on, expression) 268.114 + return self 268.115 +end 268.116 + 268.117 +function selector_prototype:set_distinct() 268.118 + if #self._distinct_on > 0 then 268.119 + error("Can not combine DISTINCT with DISTINCT ON.") 268.120 + end 268.121 + self._distinct = true 268.122 + return self 268.123 +end 268.124 + 268.125 +function selector_prototype:add_from(expression, alias, condition) 268.126 + local first = (#self._from == 0) 268.127 + if not first then 268.128 + if condition then 268.129 + add(self._from, "INNER JOIN") 268.130 + else 268.131 + add(self._from, "CROSS JOIN") 268.132 + end 268.133 + end 268.134 + if getmetatable(expression) == selector_metatable then 268.135 + if alias then 268.136 + add(self._from, {'($) AS "$"', {expression}, {alias}}) 268.137 + else 268.138 + add(self._from, {'($) AS "subquery"', {expression}}) 268.139 + end 268.140 + else 268.141 + if alias then 268.142 + add(self._from, {'$ AS "$"', {expression}, {alias}}) 268.143 + else 268.144 + add(self._from, expression) 268.145 + end 268.146 + end 268.147 + if condition then 268.148 + if first then 268.149 + self:condition(condition) 268.150 + else 268.151 + add(self._from, "ON") 268.152 + add(self._from, condition) 268.153 + end 268.154 + end 268.155 + return self 268.156 +end 268.157 + 268.158 +function selector_prototype:add_where(expression) 268.159 + add(self._where, expression) 268.160 + return self 268.161 +end 268.162 + 268.163 +function selector_prototype:add_group_by(expression) 268.164 + add(self._group_by, expression) 268.165 + return self 268.166 +end 268.167 + 268.168 +function selector_prototype:add_having(expression) 268.169 + add(self._having, expression) 268.170 + return self 268.171 +end 268.172 + 268.173 +function selector_prototype:add_combine(expression) 268.174 + add(self._combine, expression) 268.175 + return self 268.176 +end 268.177 + 268.178 +function selector_prototype:add_order_by(expression) 268.179 + add(self._order_by, expression) 268.180 + return self 268.181 +end 268.182 + 268.183 +function selector_prototype:limit(count) 268.184 + if type(count) ~= "number" or count % 1 ~= 0 then 268.185 + error("LIMIT must be an integer.") 268.186 + end 268.187 + self._limit = count 268.188 + return self 268.189 +end 268.190 + 268.191 +function selector_prototype:offset(count) 268.192 + if type(count) ~= "number" or count % 1 ~= 0 then 268.193 + error("OFFSET must be an integer.") 268.194 + end 268.195 + self._offset = count 268.196 + return self 268.197 +end 268.198 + 268.199 +function selector_prototype:reset_fields() 268.200 + for idx in ipairs(self._fields) do 268.201 + self._fields[idx] = nil 268.202 + end 268.203 + return self 268.204 +end 268.205 + 268.206 +function selector_prototype:add_field(expression, alias, options) 268.207 + if alias then 268.208 + add(self._fields, {'$ AS "$"', {expression}, {alias}}) 268.209 + else 268.210 + add(self._fields, expression) 268.211 + end 268.212 + if options then 268.213 + for i, option in ipairs(options) do 268.214 + if option == "distinct" then 268.215 + if alias then 268.216 + self:add_distinct_on('"' .. alias .. '"') 268.217 + else 268.218 + self:add_distinct_on(expression) 268.219 + end 268.220 + elseif option == "grouped" then 268.221 + if alias then 268.222 + self:add_group_by('"' .. alias .. '"') 268.223 + else 268.224 + self:add_group_by(expression) 268.225 + end 268.226 + else 268.227 + error("Unknown option '" .. option .. "' to add_field method.") 268.228 + end 268.229 + end 268.230 + end 268.231 + return self 268.232 +end 268.233 + 268.234 +function selector_prototype:join(...) -- NOTE: alias for add_from 268.235 + return self:add_from(...) 268.236 +end 268.237 + 268.238 +function selector_prototype:from(expression, alias, condition) 268.239 + if #self._from > 0 then 268.240 + error("From-clause already existing (hint: try join).") 268.241 + end 268.242 + return self:join(expression, alias, condition) 268.243 +end 268.244 + 268.245 +function selector_prototype:left_join(expression, alias, condition) 268.246 + local first = (#self._from == 0) 268.247 + if not first then 268.248 + add(self._from, "LEFT OUTER JOIN") 268.249 + end 268.250 + if alias then 268.251 + add(self._from, {'$ AS "$"', {expression}, {alias}}) 268.252 + else 268.253 + add(self._from, expression) 268.254 + end 268.255 + if condition then 268.256 + if first then 268.257 + self:condition(condition) 268.258 + else 268.259 + add(self._from, "ON") 268.260 + add(self._from, condition) 268.261 + end 268.262 + end 268.263 + return self 268.264 +end 268.265 + 268.266 +function selector_prototype:union(expression) 268.267 + self:add_combine{"UNION $", {expression}} 268.268 + return self 268.269 +end 268.270 + 268.271 +function selector_prototype:union_all(expression) 268.272 + self:add_combine{"UNION ALL $", {expression}} 268.273 + return self 268.274 +end 268.275 + 268.276 +function selector_prototype:intersect(expression) 268.277 + self:add_combine{"INTERSECT $", {expression}} 268.278 + return self 268.279 +end 268.280 + 268.281 +function selector_prototype:intersect_all(expression) 268.282 + self:add_combine{"INTERSECT ALL $", {expression}} 268.283 + return self 268.284 +end 268.285 + 268.286 +function selector_prototype:except(expression) 268.287 + self:add_combine{"EXCEPT $", {expression}} 268.288 + return self 268.289 +end 268.290 + 268.291 +function selector_prototype:except_all(expression) 268.292 + self:add_combine{"EXCEPT ALL $", {expression}} 268.293 + return self 268.294 +end 268.295 + 268.296 +function selector_prototype:set_class(class) 268.297 + self._class = class 268.298 + return self 268.299 +end 268.300 + 268.301 +function selector_prototype:attach(mode, data2, field1, field2, ref1, ref2) 268.302 + self._attach = { 268.303 + mode = mode, 268.304 + data2 = data2, 268.305 + field1 = field1, 268.306 + field2 = field2, 268.307 + ref1 = ref1, 268.308 + ref2 = ref2 268.309 + } 268.310 + return self 268.311 +end 268.312 + 268.313 +-- TODO: many-to-many relations 268.314 + 268.315 +function selector_metatable:__tostring() 268.316 + local parts = {sep = " "} 268.317 + add(parts, "SELECT") 268.318 + if self._distinct then 268.319 + add(parts, "DISTINCT") 268.320 + elseif #self._distinct_on > 0 then 268.321 + add(parts, {"DISTINCT ON ($)", self._distinct_on}) 268.322 + end 268.323 + add(parts, {"$", self._fields}) 268.324 + if #self._from > 0 then 268.325 + add(parts, {"FROM $", self._from}) 268.326 + end 268.327 + if #self._mode == "empty_list" then 268.328 + add(parts, "WHERE FALSE") 268.329 + elseif #self._where > 0 then 268.330 + add(parts, {"WHERE $", self._where}) 268.331 + end 268.332 + if #self._group_by > 0 then 268.333 + add(parts, {"GROUP BY $", self._group_by}) 268.334 + end 268.335 + if #self._having > 0 then 268.336 + add(parts, {"HAVING $", self._having}) 268.337 + end 268.338 + for i, v in ipairs(self._combine) do 268.339 + add(parts, v) 268.340 + end 268.341 + if #self._order_by > 0 then 268.342 + add(parts, {"ORDER BY $", self._order_by}) 268.343 + end 268.344 + if self._mode == "empty_list" then 268.345 + add(parts, "LIMIT 0") 268.346 + elseif self._mode ~= "list" then 268.347 + add(parts, "LIMIT 1") 268.348 + elseif self._limit then 268.349 + add(parts, "LIMIT " .. self._limit) 268.350 + end 268.351 + if self._offset then 268.352 + add(parts, "OFFSET " .. self._offset) 268.353 + end 268.354 + return self._db_conn:assemble_command{"$", parts} 268.355 +end 268.356 + 268.357 +function selector_prototype:try_exec() 268.358 + if self._mode == "empty_list" then 268.359 + if self._class then 268.360 + return nil, self._class:create_list() 268.361 + else 268.362 + return nil, self._db_conn:create_list() 268.363 + end 268.364 + end 268.365 + local db_error, db_result = self._db_conn:try_query(self, self._mode) 268.366 + if db_error then 268.367 + return db_error 268.368 + elseif db_result then 268.369 + if self._class then set_class(db_result, self._class) end 268.370 + if self._attach then 268.371 + attach( 268.372 + self._attach.mode, 268.373 + db_result, 268.374 + self._attach.data2, 268.375 + self._attach.field1, 268.376 + self._attach.field2, 268.377 + self._attach.ref1, 268.378 + self._attach.ref2 268.379 + ) 268.380 + end 268.381 + return nil, db_result 268.382 + else 268.383 + return nil 268.384 + end 268.385 +end 268.386 + 268.387 +function selector_prototype:exec() 268.388 + local db_error, result = self:try_exec() 268.389 + if db_error then 268.390 + db_error:escalate() 268.391 + else 268.392 + return result 268.393 + end 268.394 +end 268.395 + 268.396 + 268.397 + 268.398 +----------------- 268.399 +-- attachments -- 268.400 +----------------- 268.401 + 268.402 +local function attach_key(row, fields) 268.403 + local t = type(fields) 268.404 + if t == "string" then 268.405 + return tostring(row[fields]) 268.406 + elseif t == "table" then 268.407 + local r = {} 268.408 + for idx, field in ipairs(fields) do 268.409 + r[idx] = string.format("%q", row[field]) 268.410 + end 268.411 + return table.concat(r) 268.412 + else 268.413 + error("Field information for 'mondelefant.attach' is neither a string nor a table.") 268.414 + end 268.415 +end 268.416 + 268.417 +function attach(mode, data1, data2, key1, key2, ref1, ref2) 268.418 + local many1, many2 268.419 + if mode == "11" then 268.420 + many1 = false 268.421 + many2 = false 268.422 + elseif mode == "1m" then 268.423 + many1 = false 268.424 + many2 = true 268.425 + elseif mode == "m1" then 268.426 + many1 = true 268.427 + many2 = false 268.428 + elseif mode == "mm" then 268.429 + many1 = true 268.430 + many2 = true 268.431 + else 268.432 + error("Unknown mode specified for 'mondelefant.attach'.") 268.433 + end 268.434 + local list1, list2 268.435 + if data1._type == "object" then 268.436 + list1 = { data1 } 268.437 + elseif data1._type == "list" then 268.438 + list1 = data1 268.439 + else 268.440 + error("First result data given to 'mondelefant.attach' is invalid.") 268.441 + end 268.442 + if data2._type == "object" then 268.443 + list2 = { data2 } 268.444 + elseif data2._type == "list" then 268.445 + list2 = data2 268.446 + else 268.447 + error("Second result data given to 'mondelefant.attach' is invalid.") 268.448 + end 268.449 + local hash1 = {} 268.450 + local hash2 = {} 268.451 + if ref2 then 268.452 + for i, row in ipairs(list1) do 268.453 + local key = attach_key(row, key1) 268.454 + local list = hash1[key] 268.455 + if not list then list = {}; hash1[key] = list end 268.456 + list[#list + 1] = row 268.457 + end 268.458 + end 268.459 + if ref1 then 268.460 + for i, row in ipairs(list2) do 268.461 + local key = attach_key(row, key2) 268.462 + local list = hash2[key] 268.463 + if not list then list = {}; hash2[key] = list end 268.464 + list[#list + 1] = row 268.465 + end 268.466 + for i, row in ipairs(list1) do 268.467 + local key = attach_key(row, key1) 268.468 + local matching_rows = hash2[key] 268.469 + if many2 then 268.470 + local list = data2._connection:create_list(matching_rows) 268.471 + list._class = data2._class 268.472 + row._ref[ref1] = list 268.473 + elseif matching_rows and #matching_rows == 1 then 268.474 + row._ref[ref1] = matching_rows[1] 268.475 + else 268.476 + row._ref[ref1] = false 268.477 + end 268.478 + end 268.479 + end 268.480 + if ref2 then 268.481 + for i, row in ipairs(list2) do 268.482 + local key = attach_key(row, key2) 268.483 + local matching_rows = hash1[key] 268.484 + if many1 then 268.485 + local list = data1._connection:create_list(matching_rows) 268.486 + list._class = data1._class 268.487 + row._ref[ref2] = list 268.488 + elseif matching_rows and #matching_rows == 1 then 268.489 + row._ref[ref2] = matching_rows[1] 268.490 + else 268.491 + row._ref[ref2] = false 268.492 + end 268.493 + end 268.494 + end 268.495 +end 268.496 + 268.497 + 268.498 + 268.499 +------------------ 268.500 +-- model system -- 268.501 +------------------ 268.502 + 268.503 +class_prototype.primary_key = "id" 268.504 + 268.505 +function class_prototype:get_db_conn() 268.506 + error( 268.507 + "Method mondelefant class(_prototype):get_db_conn() " .. 268.508 + "has to be implemented." 268.509 + ) 268.510 +end 268.511 + 268.512 +function class_prototype:get_qualified_table() 268.513 + if not self.table then error "Table unknown." end 268.514 + if self.schema then 268.515 + return '"' .. self.schema .. '"."' .. self.table .. '"' 268.516 + else 268.517 + return '"' .. self.table .. '"' 268.518 + end 268.519 +end 268.520 + 268.521 +function class_prototype:get_qualified_table_literal() 268.522 + if not self.table then error "Table unknown." end 268.523 + if self.schema then 268.524 + return self.schema .. '.' .. self.table 268.525 + else 268.526 + return self.table 268.527 + end 268.528 +end 268.529 + 268.530 +function class_prototype:get_primary_key_list() 268.531 + local primary_key = self.primary_key 268.532 + if type(primary_key) == "string" then 268.533 + return {primary_key} 268.534 + else 268.535 + return primary_key 268.536 + end 268.537 +end 268.538 + 268.539 +function class_prototype:get_columns() 268.540 + if self._columns then 268.541 + return self._columns 268.542 + end 268.543 + local selector = self:get_db_conn():new_selector() 268.544 + selector:set_class(self) 268.545 + selector:from(self:get_qualified_table()) 268.546 + selector:add_field("*") 268.547 + selector:add_where("FALSE") 268.548 + local db_result = selector:exec() 268.549 + local connection = db_result._connection 268.550 + local columns = {} 268.551 + for idx, info in ipairs(db_result._column_info) do 268.552 + local key = info.field_name 268.553 + local value = { 268.554 + name = key, 268.555 + type = connection.type_mappings[info.type] 268.556 + } 268.557 + columns[key] = value 268.558 + table.insert(columns, value) 268.559 + end 268.560 + self._columns = columns 268.561 + return columns 268.562 +end 268.563 + 268.564 +function class_prototype:new_selector(db_conn) 268.565 + local selector = (db_conn or self:get_db_conn()):new_selector() 268.566 + selector:set_class(self) 268.567 + selector:from(self:get_qualified_table()) 268.568 + selector:add_field(self:get_qualified_table() .. ".*") 268.569 + return selector 268.570 +end 268.571 + 268.572 +function class_prototype:create_list() 268.573 + local list = self:get_db_conn():create_list() 268.574 + list._class = self 268.575 + return list 268.576 +end 268.577 + 268.578 +function class_prototype:new() 268.579 + local object = self:get_db_conn():create_object() 268.580 + object._class = self 268.581 + object._new = true 268.582 + return object 268.583 +end 268.584 + 268.585 +function class_prototype.object:try_save() 268.586 + if not self._class then 268.587 + error("Cannot save object: No class information available.") 268.588 + end 268.589 + local primary_key = self._class:get_primary_key_list() 268.590 + local primary_key_sql = { sep = ", " } 268.591 + for idx, value in ipairs(primary_key) do 268.592 + primary_key_sql[idx] = '"' .. value .. '"' 268.593 + end 268.594 + if self._new then 268.595 + local fields = {sep = ", "} 268.596 + local values = {sep = ", "} 268.597 + for key, dummy in pairs(self._dirty or {}) do 268.598 + add(fields, {'"$"', {key}}) 268.599 + add(values, {'?', self[key]}) 268.600 + end 268.601 + if compat_returning then -- compatibility for PostgreSQL 8.1 268.602 + local db_error, db_result1, db_result2 = self._connection:try_query( 268.603 + { 268.604 + 'INSERT INTO $ ($) VALUES ($)', 268.605 + {self._class:get_qualified_table()}, 268.606 + fields, 268.607 + values, 268.608 + primary_key_sql 268.609 + }, 268.610 + "list", 268.611 + { 268.612 + 'SELECT currval(?)', 268.613 + self._class.table .. '_id_seq' 268.614 + }, 268.615 + "object" 268.616 + ) 268.617 + if db_error then 268.618 + return db_error 268.619 + end 268.620 + self.id = db_result2.id 268.621 + else 268.622 + local db_error, db_result = self._connection:try_query( 268.623 + { 268.624 + 'INSERT INTO $ ($) VALUES ($) RETURNING ($)', 268.625 + {self._class:get_qualified_table()}, 268.626 + fields, 268.627 + values, 268.628 + primary_key_sql 268.629 + }, 268.630 + "object" 268.631 + ) 268.632 + if db_error then 268.633 + return db_error 268.634 + end 268.635 + for idx, value in ipairs(primary_key) do 268.636 + self[value] = db_result[value] 268.637 + end 268.638 + end 268.639 + self._new = false 268.640 + else 268.641 + local command_sets = {sep = ", "} 268.642 + for key, dummy in pairs(self._dirty or {}) do 268.643 + add(command_sets, {'"$" = ?', {key}, self[key]}) 268.644 + end 268.645 + if #command_sets >= 1 then 268.646 + local primary_key_compare = {sep = " AND "} 268.647 + for idx, value in ipairs(primary_key) do 268.648 + primary_key_compare[idx] = { 268.649 + "$ = ?", 268.650 + {'"' .. value .. '"'}, 268.651 + self[value] 268.652 + } 268.653 + end 268.654 + local db_error = self._connection:try_query{ 268.655 + 'UPDATE $ SET $ WHERE $', 268.656 + {self._class:get_qualified_table()}, 268.657 + command_sets, 268.658 + primary_key_compare 268.659 + } 268.660 + if db_error then 268.661 + return db_error 268.662 + end 268.663 + end 268.664 + end 268.665 + return nil 268.666 +end 268.667 + 268.668 +function class_prototype.object:save() 268.669 + local db_error = self:try_save() 268.670 + if db_error then 268.671 + db_error:escalate() 268.672 + end 268.673 + return self 268.674 +end 268.675 + 268.676 +function class_prototype.object:try_destroy() 268.677 + if not self._class then 268.678 + error("Cannot destroy object: No class information available.") 268.679 + end 268.680 + local primary_key = self._class:get_primary_key_list() 268.681 + local primary_key_compare = {sep = " AND "} 268.682 + for idx, value in ipairs(primary_key) do 268.683 + primary_key_compare[idx] = { 268.684 + "$ = ?", 268.685 + {'"' .. value .. '"'}, 268.686 + self[value] 268.687 + } 268.688 + end 268.689 + return self._connection:try_query{ 268.690 + 'DELETE FROM $ WHERE $', 268.691 + {self._class:get_qualified_table()}, 268.692 + primary_key_compare 268.693 + } 268.694 +end 268.695 + 268.696 +function class_prototype.object:destroy() 268.697 + local db_error = self:try_destroy() 268.698 + if db_error then 268.699 + db_error:escalate() 268.700 + end 268.701 + return self 268.702 +end 268.703 + 268.704 +function class_prototype.list:get_reference_selector( 268.705 + ref_name, options, ref_alias, back_ref_alias 268.706 +) 268.707 + local ref_info = self._class.references[ref_name] 268.708 + if not ref_info then 268.709 + error('Reference with name "' .. ref_name .. '" not found.') 268.710 + end 268.711 + local selector = ref_info.selector_generator(self, options or {}) 268.712 + local mode = ref_info.mode 268.713 + if mode == "mm" or mode == "1m" then 268.714 + mode = "m1" 268.715 + elseif mode == "m1" then 268.716 + mode = "1m" 268.717 + end 268.718 + local ref_alias = ref_alias 268.719 + if ref_alias == false then 268.720 + ref_alias = nil 268.721 + elseif ref_alias == nil then 268.722 + ref_alias = ref_name 268.723 + end 268.724 + local back_ref_alias 268.725 + if back_ref_alias == false then 268.726 + back_ref_alias = nil 268.727 + elseif back_ref_alias == nil then 268.728 + back_ref_alias = ref_info.back_ref 268.729 + end 268.730 + selector:attach( 268.731 + mode, 268.732 + self, 268.733 + ref_info.that_key, ref_info.this_key, 268.734 + back_ref_alias or ref_info.back_ref, ref_alias or ref_name 268.735 + ) 268.736 + return selector 268.737 +end 268.738 + 268.739 +function class_prototype.list.load(...) 268.740 + return class_prototype.list.get_reference_selector(...):exec() 268.741 +end 268.742 + 268.743 +function class_prototype.object:get_reference_selector(...) 268.744 + local list = self._class:create_list() 268.745 + list[1] = self 268.746 + return list:get_reference_selector(...) 268.747 +end 268.748 + 268.749 +function class_prototype.object.load(...) 268.750 + return class_prototype.object.get_reference_selector(...):exec() 268.751 +end 268.752 + 268.753 + 268.754 +function class_prototype:add_reference(args) 268.755 + local selector_generator = args.selector_generator 268.756 + local mode = args.mode 268.757 + local to = args.to 268.758 + local this_key = args.this_key 268.759 + local that_key = args.that_key 268.760 + local connected_by_table = args.connected_by_table -- TODO: split to table and schema 268.761 + local connected_by_this_key = args.connected_by_this_key 268.762 + local connected_by_that_key = args.connected_by_that_key 268.763 + local ref = args.ref 268.764 + local back_ref = args.back_ref 268.765 + local default_order = args.default_order 268.766 + local model 268.767 + local function get_model() 268.768 + if not model then 268.769 + if type(to) == "string" then 268.770 + model = _G 268.771 + for path_element in string.gmatch(to, "[^.]+") do 268.772 + model = model[path_element] 268.773 + end 268.774 + elseif type(to) == "function" then 268.775 + model = to() 268.776 + else 268.777 + model = to 268.778 + end 268.779 + end 268.780 + if not model or model == _G then 268.781 + error("Could not get model for reference.") 268.782 + end 268.783 + return model 268.784 + end 268.785 + self.references[ref] = { 268.786 + mode = mode, 268.787 + this_key = this_key, 268.788 + that_key = connected_by_table and "mm_ref_" or that_key, 268.789 + ref = ref, 268.790 + back_ref = back_ref, 268.791 + selector_generator = selector_generator or function(list, options) 268.792 + -- TODO: support tuple keys 268.793 + local options = options or {} 268.794 + local model = get_model() 268.795 + -- TODO: too many records cause PostgreSQL command stack overflow 268.796 + local ids = { sep = ", " } 268.797 + for i, object in ipairs(list) do 268.798 + local id = object[this_key] 268.799 + if id ~= nil then 268.800 + ids[#ids+1] = {"?", id} 268.801 + end 268.802 + end 268.803 + if #ids == 0 then 268.804 + return model:new_selector():empty_list_mode() 268.805 + end 268.806 + local selector = model:new_selector() 268.807 + if connected_by_table then 268.808 + selector:join( 268.809 + connected_by_table, 268.810 + nil, 268.811 + { 268.812 + '$."$" = $."$"', 268.813 + {connected_by_table}, 268.814 + {connected_by_that_key}, 268.815 + {model:get_qualified_table()}, 268.816 + {that_key} 268.817 + } 268.818 + ) 268.819 + selector:add_field( 268.820 + { 268.821 + '$."$"', 268.822 + {connected_by_table}, 268.823 + {connected_by_this_key} 268.824 + }, 268.825 + 'mm_ref_' 268.826 + ) 268.827 + selector:add_where{ 268.828 + '$."$" IN ($)', 268.829 + {connected_by_table}, 268.830 + {connected_by_this_key}, 268.831 + ids 268.832 + } 268.833 + else 268.834 + selector:add_where{'"$" IN ($)', {that_key}, ids} 268.835 + end 268.836 + if options.order == nil and default_order then 268.837 + selector:add_order_by(default_order) 268.838 + elseif options.order then 268.839 + selector:add_order_by(options.order) 268.840 + end 268.841 + return selector 268.842 + end 268.843 + } 268.844 + if mode == "m1" or mode == "11" then 268.845 + self.foreign_keys[this_key] = ref 268.846 + end 268.847 + return self 268.848 +end
269.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 269.2 +++ b/libraries/mondelefant/mondelefant_atom_connector.lua Sun Oct 25 12:00:00 2009 +0100 269.3 @@ -0,0 +1,189 @@ 269.4 +#!/usr/bin/env lua 269.5 + 269.6 +local _G = _G 269.7 +local _VERSION = _VERSION 269.8 +local assert = assert 269.9 +local collectgarbage = collectgarbage 269.10 +local dofile = dofile 269.11 +local error = error 269.12 +local getfenv = getfenv 269.13 +local getmetatable = getmetatable 269.14 +local ipairs = ipairs 269.15 +local load = load 269.16 +local loadfile = loadfile 269.17 +local loadstring = loadstring 269.18 +local module = module 269.19 +local next = next 269.20 +local pairs = pairs 269.21 +local pcall = pcall 269.22 +local print = print 269.23 +local rawequal = rawequal 269.24 +local rawget = rawget 269.25 +local rawset = rawset 269.26 +local require = require 269.27 +local select = select 269.28 +local setfenv = setfenv 269.29 +local setmetatable = setmetatable 269.30 +local tonumber = tonumber 269.31 +local tostring = tostring 269.32 +local type = type 269.33 +local unpack = unpack 269.34 +local xpcall = xpcall 269.35 + 269.36 +local coroutine = coroutine 269.37 +local debug = debug 269.38 +local io = io 269.39 +local math = math 269.40 +local os = os 269.41 +local package = package 269.42 +local string = string 269.43 + 269.44 +local mondelefant = require("mondelefant") 269.45 +local atom = require("atom") 269.46 + 269.47 +module(...) 269.48 + 269.49 + 269.50 +input_converters = setmetatable({}, { __mode = "k" }) 269.51 + 269.52 +input_converters["boolean"] = function(conn, value) 269.53 + if value then return "TRUE" else return "FALSE" end 269.54 +end 269.55 + 269.56 +input_converters["number"] = function(conn, value) 269.57 + local str = tostring(value) 269.58 + if string.find(str, "^[0-9%.e%-]+$") then 269.59 + return str 269.60 + else 269.61 + return "'NaN'" 269.62 + end 269.63 +end 269.64 + 269.65 +input_converters[atom.fraction] = function(conn, value) 269.66 + if value.invalid then 269.67 + return "'NaN'" 269.68 + else 269.69 + local n, d = tostring(value.numerator), tostring(value.denominator) 269.70 + if string.find(n, "^%-?[0-9]+$") and string.find(d, "^%-?[0-9]+$") then 269.71 + return "(" .. n .. "::numeric / " .. d .. "::numeric)" 269.72 + else 269.73 + return "'NaN'" 269.74 + end 269.75 + end 269.76 +end 269.77 + 269.78 +input_converters[atom.date] = function(conn, value) 269.79 + return conn:quote_string(tostring(value)) .. "::date" 269.80 +end 269.81 + 269.82 +input_converters[atom.timestamp] = function(conn, value) 269.83 + return conn:quote_string(tostring(value)) -- don't define type 269.84 +end 269.85 + 269.86 +input_converters[atom.time] = function(conn, value) 269.87 + return conn:quote_string(tostring(value)) .. "::time" 269.88 +end 269.89 + 269.90 + 269.91 +output_converters = setmetatable({}, { __mode = "k" }) 269.92 + 269.93 +output_converters.int8 = function(str) return atom.integer:load(str) end 269.94 +output_converters.int4 = function(str) return atom.integer:load(str) end 269.95 +output_converters.int2 = function(str) return atom.integer:load(str) end 269.96 + 269.97 +output_converters.numeric = function(str) return atom.number:load(str) end 269.98 +output_converters.float4 = function(str) return atom.number:load(str) end 269.99 +output_converters.float8 = function(str) return atom.number:load(str) end 269.100 + 269.101 +output_converters.bool = function(str) return atom.boolean:load(str) end 269.102 + 269.103 +output_converters.date = function(str) return atom.date:load(str) end 269.104 + 269.105 +local timestamp_loader_func = function(str) 269.106 + local hour, minute, second = string.match( 269.107 + str, 269.108 + "^([0-9]?[0-9]):([0-9][0-9]):([0-9][0-9])" 269.109 + ) 269.110 + if hour then 269.111 + return atom.timestamp{ 269.112 + hour = tonumber(hour), 269.113 + minute = tonumber(minute), 269.114 + second = tonumber(second) 269.115 + } 269.116 + else 269.117 + return atom.timestamp.invalid 269.118 + end 269.119 +end 269.120 +output_converters.timestamp = timestamp_loader_func 269.121 +output_converters.timestamptz = timestamp_loader_func 269.122 + 269.123 +local time_loader_func = function(str) 269.124 + local year, month, day, hour, minute, second = string.match( 269.125 + str, 269.126 + "^([0-9][0-9][0-9][0-9])%-([0-9][0-9])%-([0-9][0-9]) ([0-9]?[0-9]):([0-9][0-9]):([0-9][0-9])" 269.127 + ) 269.128 + if year then 269.129 + return atom.time{ 269.130 + year = tonumber(year), 269.131 + month = tonumber(month), 269.132 + day = tonumber(day), 269.133 + hour = tonumber(hour), 269.134 + minute = tonumber(minute), 269.135 + second = tonumber(second) 269.136 + } 269.137 + else 269.138 + return atom.time.invalid 269.139 + end 269.140 +end 269.141 +output_converters.time = time_loader_func 269.142 +output_converters.timetz = time_loader_func 269.143 + 269.144 +mondelefant.postgresql_connection_prototype.type_mappings = { 269.145 + int8 = atom.integer, 269.146 + int4 = atom.integer, 269.147 + int2 = atom.integer, 269.148 + bool = atom.boolean, 269.149 + date = atom.date, 269.150 + timestamp = atom.timestamp, 269.151 + time = atom.time, 269.152 + text = atom.string, 269.153 + varchar = atom.string, 269.154 +} 269.155 + 269.156 + 269.157 +function mondelefant.postgresql_connection_prototype.input_converter(conn, value, info) 269.158 + if value == nil then 269.159 + return "NULL" 269.160 + else 269.161 + local converter = 269.162 + input_converters[getmetatable(value)] or 269.163 + input_converters[type(value)] 269.164 + if converter then 269.165 + return converter(conn, value) 269.166 + else 269.167 + return conn:quote_string(tostring(value)) 269.168 + end 269.169 + end 269.170 +end 269.171 + 269.172 +function mondelefant.postgresql_connection_prototype.output_converter(conn, value, info) 269.173 + if value == nil then 269.174 + return nil 269.175 + else 269.176 + local converter = output_converters[info.type] 269.177 + if converter then 269.178 + return converter(value) 269.179 + else 269.180 + return value 269.181 + end 269.182 + end 269.183 +end 269.184 + 269.185 + 269.186 +--[[ 269.187 + 269.188 +db = assert(mondelefant.connect{engine='postgresql', dbname='test'}) 269.189 +result = db:query{'SELECT ? + 1', atom.date{ year=1999, month=12, day=31}} 269.190 +print(result[1][1].year) 269.191 + 269.192 +--]]
270.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 270.2 +++ b/libraries/mondelefant/mondelefant_native.c Sun Oct 25 12:00:00 2009 +0100 270.3 @@ -0,0 +1,1558 @@ 270.4 +#include <lua.h> 270.5 +#include <lauxlib.h> 270.6 +#include <libpq-fe.h> 270.7 +#include <postgres.h> 270.8 +#include <catalog/pg_type.h> 270.9 +#include <stdint.h> 270.10 + 270.11 +#define MONDELEFANT_REGKEY "e449ba8d9a53d353_mondelefant" 270.12 + 270.13 +#define MONDELEFANT_MODULE_REGKEY (MONDELEFANT_REGKEY "_module") 270.14 +#define MONDELEFANT_CONN_MT_REGKEY (MONDELEFANT_REGKEY "_connection") 270.15 +#define MONDELEFANT_CONN_DATA_REGKEY (MONDELEFANT_REGKEY "_connection_data") 270.16 +#define MONDELEFANT_RESULT_MT_REGKEY (MONDELEFANT_REGKEY "_result") 270.17 +#define MONDELEFANT_ERROROBJECT_MT_REGKEY (MONDELEFANT_REGKEY "_errorobject") 270.18 +#define MONDELEFANT_CLASS_MT_REGKEY (MONDELEFANT_REGKEY "_class") 270.19 +#define MONDELEFANT_CLASS_PROTO_REGKEY (MONDELEFANT_REGKEY "_class_proto") 270.20 + 270.21 +#define MONDELEFANT_SERVER_ENCODING_ASCII 0 270.22 +#define MONDELEFANT_SERVER_ENCODING_UTF8 1 270.23 + 270.24 +typedef struct { 270.25 + PGconn *pgconn; 270.26 + int server_encoding; 270.27 +} mondelefant_conn_t; 270.28 + 270.29 +static size_t utf8_position_to_byte(const char *str, size_t utf8pos) { 270.30 + size_t bytepos; 270.31 + for (bytepos = 0; utf8pos > 0; bytepos++) { 270.32 + uint8_t c; 270.33 + c = ((const uint8_t *)str)[bytepos]; 270.34 + if (!c) break; 270.35 + if (c <= 0x7f || c >= 0xc0) utf8pos--; 270.36 + } 270.37 + return bytepos; 270.38 +} 270.39 + 270.40 +#define MONDELEFANT_POSTGRESQL_BINARY_OID ((Oid)17) 270.41 + 270.42 +static const char *mondelefant_oid_to_typestr(Oid oid) { 270.43 + switch (oid) { 270.44 + case 16: return "bool"; 270.45 + case 17: return "bytea"; 270.46 + case 18: return "char"; 270.47 + case 19: return "name"; 270.48 + case 20: return "int8"; 270.49 + case 21: return "int2"; 270.50 + case 23: return "int4"; 270.51 + case 25: return "text"; 270.52 + case 26: return "oid"; 270.53 + case 27: return "tid"; 270.54 + case 28: return "xid"; 270.55 + case 29: return "cid"; 270.56 + case 600: return "point"; 270.57 + case 601: return "lseg"; 270.58 + case 602: return "path"; 270.59 + case 603: return "box"; 270.60 + case 604: return "polygon"; 270.61 + case 628: return "line"; 270.62 + case 700: return "float4"; 270.63 + case 701: return "float8"; 270.64 + case 705: return "unknown"; 270.65 + case 718: return "circle"; 270.66 + case 790: return "money"; 270.67 + case 829: return "macaddr"; 270.68 + case 869: return "inet"; 270.69 + case 650: return "cidr"; 270.70 + case 1042: return "bpchar"; 270.71 + case 1043: return "varchar"; 270.72 + case 1082: return "date"; 270.73 + case 1083: return "time"; 270.74 + case 1114: return "timestamp"; 270.75 + case 1184: return "timestamptz"; 270.76 + case 1186: return "interval"; 270.77 + case 1266: return "timetz"; 270.78 + case 1560: return "bit"; 270.79 + case 1562: return "varbit"; 270.80 + case 1700: return "numeric"; 270.81 + default: return NULL; 270.82 + } 270.83 +} 270.84 + 270.85 +#define mondelefant_errcode_item(incode, outcode) \ 270.86 + if (!strncmp(pgcode, (incode), strlen(incode))) return outcode; else 270.87 + 270.88 +#define MONDELEFANT_ERRCODE_UNKNOWN "unknown" 270.89 +#define MONDELEFANT_ERRCODE_CONNECTION "ConnectionException" 270.90 +#define MONDELEFANT_ERRCODE_RESULTCOUNT_LOW "WrongResultSetCount.ResultSetMissing" 270.91 +#define MONDELEFANT_ERRCODE_RESULTCOUNT_HIGH "WrongResultSetCount.TooManyResults" 270.92 +#define MONDELEFANT_ERRCODE_QUERY1_NO_ROWS "NoData.OneRowExpected" 270.93 +#define MONDELEFANT_ERRCODE_QUERY1_MULTIPLE_ROWS "CardinalityViolation.OneRowExpected" 270.94 + 270.95 +static const char *mondelefant_translate_errcode(const char *pgcode) { 270.96 + if (!pgcode) abort(); // should not happen 270.97 + mondelefant_errcode_item("02", "NoData") 270.98 + mondelefant_errcode_item("03", "SqlStatementNotYetComplete") 270.99 + mondelefant_errcode_item("08", "ConnectionException") 270.100 + mondelefant_errcode_item("09", "TriggeredActionException") 270.101 + mondelefant_errcode_item("0A", "FeatureNotSupported") 270.102 + mondelefant_errcode_item("0B", "InvalidTransactionInitiation") 270.103 + mondelefant_errcode_item("0F", "LocatorException") 270.104 + mondelefant_errcode_item("0L", "InvalidGrantor") 270.105 + mondelefant_errcode_item("0P", "InvalidRoleSpecification") 270.106 + mondelefant_errcode_item("21", "CardinalityViolation") 270.107 + mondelefant_errcode_item("22", "DataException") 270.108 + mondelefant_errcode_item("23001", "IntegrityConstraintViolation.RestrictViolation") 270.109 + mondelefant_errcode_item("23502", "IntegrityConstraintViolation.NotNullViolation") 270.110 + mondelefant_errcode_item("23503", "IntegrityConstraintViolation.ForeignKeyViolation") 270.111 + mondelefant_errcode_item("23505", "IntegrityConstraintViolation.UniqueViolation") 270.112 + mondelefant_errcode_item("23514", "IntegrityConstraintViolation.CheckViolation") 270.113 + mondelefant_errcode_item("23", "IntegrityConstraintViolation") 270.114 + mondelefant_errcode_item("24", "InvalidCursorState") 270.115 + mondelefant_errcode_item("25", "InvalidTransactionState") 270.116 + mondelefant_errcode_item("26", "InvalidSqlStatementName") 270.117 + mondelefant_errcode_item("27", "TriggeredDataChangeViolation") 270.118 + mondelefant_errcode_item("28", "InvalidAuthorizationSpecification") 270.119 + mondelefant_errcode_item("2B", "DependentPrivilegeDescriptorsStillExist") 270.120 + mondelefant_errcode_item("2D", "InvalidTransactionTermination") 270.121 + mondelefant_errcode_item("2F", "SqlRoutineException") 270.122 + mondelefant_errcode_item("34", "InvalidCursorName") 270.123 + mondelefant_errcode_item("38", "ExternalRoutineException") 270.124 + mondelefant_errcode_item("39", "ExternalRoutineInvocationException") 270.125 + mondelefant_errcode_item("3B", "SavepointException") 270.126 + mondelefant_errcode_item("3D", "InvalidCatalogName") 270.127 + mondelefant_errcode_item("3F", "InvalidSchemaName") 270.128 + mondelefant_errcode_item("40", "TransactionRollback") 270.129 + mondelefant_errcode_item("42", "SyntaxErrorOrAccessRuleViolation") 270.130 + mondelefant_errcode_item("44", "WithCheckOptionViolation") 270.131 + mondelefant_errcode_item("53", "InsufficientResources") 270.132 + mondelefant_errcode_item("54", "ProgramLimitExceeded") 270.133 + mondelefant_errcode_item("55", "ObjectNotInPrerequisiteState") 270.134 + mondelefant_errcode_item("57", "OperatorIntervention") 270.135 + mondelefant_errcode_item("58", "SystemError") 270.136 + mondelefant_errcode_item("F0", "ConfigurationFileError") 270.137 + mondelefant_errcode_item("P0", "PlpgsqlError") 270.138 + mondelefant_errcode_item("XX", "InternalError") 270.139 + return "unknown"; 270.140 +} 270.141 + 270.142 +static int mondelefant_check_error_class( 270.143 + const char *errcode, const char *errclass 270.144 +) { 270.145 + size_t i = 0; 270.146 + while (1) { 270.147 + if (errclass[i] == 0) { 270.148 + if (errcode[i] == 0 || errcode[i] == '.') return 1; 270.149 + else return 0; 270.150 + } 270.151 + if (errcode[i] != errclass[i]) return 0; 270.152 + i++; 270.153 + } 270.154 +} 270.155 + 270.156 +static void mondelefant_push_first_line(lua_State *L, const char *str) { 270.157 + char *str2; 270.158 + size_t i = 0; 270.159 + if (!str) abort(); // should not happen 270.160 + str2 = strdup(str); 270.161 + while (1) { 270.162 + char c = str2[i]; 270.163 + if (c == '\n' || c == '\r' || c == 0) { str2[i] = 0; break; } 270.164 + i++; 270.165 + }; 270.166 + lua_pushstring(L, str2); 270.167 + free(str2); 270.168 +} 270.169 + 270.170 +static int mondelefant_connect(lua_State *L) { 270.171 + luaL_Buffer buf; 270.172 + const char *conninfo; 270.173 + PGconn *pgconn; 270.174 + mondelefant_conn_t *conn; 270.175 + lua_settop(L, 1); 270.176 + lua_getfield(L, 1, "engine"); // 2 270.177 + if (!lua_toboolean(L, 2)) { 270.178 + return luaL_error(L, "No database engine selected."); 270.179 + } 270.180 + lua_pushliteral(L, "postgresql"); // 3 270.181 + if (!lua_rawequal(L, 2, 3)) { 270.182 + return luaL_error(L, 270.183 + "Only database engine 'postgresql' is supported." 270.184 + ); 270.185 + } 270.186 + lua_settop(L, 1); 270.187 + lua_pushnil(L); // slot for key at stack position 2 270.188 + lua_pushnil(L); // slot for value at stack position 3 270.189 + luaL_buffinit(L, &buf); 270.190 + { 270.191 + int need_seperator = 0; 270.192 + while (lua_pushvalue(L, 2), lua_next(L, 1)) { 270.193 + lua_replace(L, 3); 270.194 + lua_replace(L, 2); 270.195 + // NOTE: numbers will be converted to strings automatically here, 270.196 + // but perhaps this will change in future versions of lua 270.197 + luaL_argcheck(L, 270.198 + lua_isstring(L, 2) && lua_isstring(L, 3), 1, "non-string contained" 270.199 + ); 270.200 + lua_pushvalue(L, 2); 270.201 + lua_pushliteral(L, "engine"); 270.202 + if (!lua_rawequal(L, -2, -1)) { 270.203 + const char *value; 270.204 + size_t value_len; 270.205 + size_t value_pos = 0; 270.206 + lua_pop(L, 1); 270.207 + if (need_seperator) luaL_addchar(&buf, ' '); 270.208 + luaL_addvalue(&buf); 270.209 + luaL_addchar(&buf, '='); 270.210 + luaL_addchar(&buf, '\''); 270.211 + value = lua_tolstring(L, 3, &value_len); 270.212 + do { 270.213 + char c; 270.214 + c = value[value_pos++]; 270.215 + if (c == '\'') luaL_addchar(&buf, '\\'); 270.216 + luaL_addchar(&buf, c); 270.217 + } while (value_pos < value_len); 270.218 + luaL_addchar(&buf, '\''); 270.219 + need_seperator = 1; 270.220 + } else { 270.221 + lua_pop(L, 1); 270.222 + } 270.223 + } 270.224 + } 270.225 + luaL_pushresult(&buf); 270.226 + lua_replace(L, 2); 270.227 + lua_settop(L, 2); 270.228 + conninfo = lua_tostring(L, 2); 270.229 + pgconn = PQconnectdb(conninfo); 270.230 + if (!pgconn) { 270.231 + return luaL_error(L, 270.232 + "Error in libpq while creating 'PGconn' structure." 270.233 + ); 270.234 + } 270.235 + if (PQstatus(pgconn) != CONNECTION_OK) { 270.236 + const char *errmsg; 270.237 + lua_pushnil(L); 270.238 + errmsg = PQerrorMessage(pgconn); 270.239 + if (errmsg) { 270.240 + mondelefant_push_first_line(L, errmsg); 270.241 + } else { 270.242 + lua_pushliteral(L, 270.243 + "Error while connecting to database, but no error message given." 270.244 + ); 270.245 + } 270.246 + lua_pushliteral(L, MONDELEFANT_ERRCODE_CONNECTION); 270.247 + PQfinish(pgconn); 270.248 + return 3; 270.249 + } 270.250 + lua_settop(L, 0); 270.251 + conn = lua_newuserdata(L, sizeof(*conn)); // 1 270.252 + conn->pgconn = pgconn; 270.253 + { 270.254 + const char *charset; 270.255 + charset = PQparameterStatus(pgconn, "server_encoding"); 270.256 + if (charset && !strcmp(charset, "UTF8")) { 270.257 + conn->server_encoding = MONDELEFANT_SERVER_ENCODING_UTF8; 270.258 + } else { 270.259 + conn->server_encoding = MONDELEFANT_SERVER_ENCODING_ASCII; 270.260 + } 270.261 + } 270.262 + 270.263 + luaL_getmetatable(L, MONDELEFANT_CONN_MT_REGKEY); // 2 270.264 + lua_setmetatable(L, 1); 270.265 + 270.266 + lua_getfield(L, LUA_REGISTRYINDEX, MONDELEFANT_CONN_DATA_REGKEY); // 2 270.267 + lua_pushvalue(L, 1); // 3 270.268 + lua_newtable(L); // 4 270.269 + lua_settable(L, 2); 270.270 + lua_settop(L, 1); 270.271 + 270.272 + lua_pushliteral(L, "postgresql"); 270.273 + lua_setfield(L, 1, "engine"); 270.274 + return 1; 270.275 +} 270.276 + 270.277 +static mondelefant_conn_t *mondelefant_get_conn(lua_State *L, int index) { 270.278 + mondelefant_conn_t *conn; 270.279 + conn = luaL_checkudata(L, index, MONDELEFANT_CONN_MT_REGKEY); 270.280 + if (!conn->pgconn) { 270.281 + luaL_error(L, "PostgreSQL connection has been closed."); 270.282 + return NULL; 270.283 + } 270.284 + return conn; 270.285 +} 270.286 + 270.287 +static int mondelefant_conn_index(lua_State *L) { 270.288 + lua_settop(L, 2); 270.289 + lua_getfield(L, LUA_REGISTRYINDEX, MONDELEFANT_CONN_DATA_REGKEY); // 3 270.290 + lua_pushvalue(L, 1); // 4 270.291 + lua_gettable(L, 3); // 4 270.292 + lua_remove(L, 3); // connection specific data-table at stack position 3 270.293 + lua_pushvalue(L, 2); // 4 270.294 + lua_gettable(L, 3); // 4 270.295 + if (!lua_isnil(L, 4)) return 1; 270.296 + lua_settop(L, 3); 270.297 + lua_getfield(L, 3, "prototype"); // 4 270.298 + if (lua_toboolean(L, 4)) { 270.299 + lua_pushvalue(L, 2); // 5 270.300 + lua_gettable(L, 4); // 5 270.301 + if (!lua_isnil(L, 5)) return 1; 270.302 + } 270.303 + lua_settop(L, 2); 270.304 + lua_getfield(L, LUA_REGISTRYINDEX, MONDELEFANT_MODULE_REGKEY); // 3 270.305 + lua_getfield(L, 3, "postgresql_connection_prototype"); // 4 270.306 + if (lua_toboolean(L, 4)) { 270.307 + lua_pushvalue(L, 2); // 5 270.308 + lua_gettable(L, 4); // 5 270.309 + if (!lua_isnil(L, 5)) return 1; 270.310 + } 270.311 + lua_settop(L, 3); 270.312 + lua_getfield(L, 3, "connection_prototype"); // 4 270.313 + if (lua_toboolean(L, 4)) { 270.314 + lua_pushvalue(L, 2); // 5 270.315 + lua_gettable(L, 4); // 5 270.316 + if (!lua_isnil(L, 5)) return 1; 270.317 + } 270.318 + return 0; 270.319 +} 270.320 + 270.321 +static int mondelefant_conn_newindex(lua_State *L) { 270.322 + lua_settop(L, 3); 270.323 + lua_getfield(L, LUA_REGISTRYINDEX, MONDELEFANT_CONN_DATA_REGKEY); // 4 270.324 + lua_pushvalue(L, 1); // 5 270.325 + lua_gettable(L, 4); // 5 270.326 + lua_remove(L, 4); // connection specific data-table at stack position 4 270.327 + lua_pushvalue(L, 2); 270.328 + lua_pushvalue(L, 3); 270.329 + lua_settable(L, 4); 270.330 + return 0; 270.331 +} 270.332 + 270.333 +static int mondelefant_conn_free(lua_State *L) { 270.334 + mondelefant_conn_t *conn; 270.335 + conn = luaL_checkudata(L, 1, MONDELEFANT_CONN_MT_REGKEY); 270.336 + if (conn->pgconn) PQfinish(conn->pgconn); 270.337 + conn->pgconn = NULL; 270.338 + return 0; 270.339 +} 270.340 + 270.341 +static int mondelefant_conn_close(lua_State *L) { 270.342 + mondelefant_conn_t *conn; 270.343 + lua_settop(L, 1); 270.344 + conn = mondelefant_get_conn(L, 1); 270.345 + PQfinish(conn->pgconn); 270.346 + conn->pgconn = NULL; 270.347 + return 0; 270.348 +} 270.349 + 270.350 +static int mondelefant_conn_is_ok(lua_State *L) { 270.351 + mondelefant_conn_t *conn; 270.352 + lua_settop(L, 1); 270.353 + conn = mondelefant_get_conn(L, 1); 270.354 + lua_pushboolean(L, PQstatus(conn->pgconn) == CONNECTION_OK); 270.355 + return 1; 270.356 +} 270.357 + 270.358 +static int mondelefant_conn_get_transaction_status(lua_State *L) { 270.359 + mondelefant_conn_t *conn; 270.360 + lua_settop(L, 1); 270.361 + conn = mondelefant_get_conn(L, 1); 270.362 + switch (PQtransactionStatus(conn->pgconn)) { 270.363 + case PQTRANS_IDLE: 270.364 + lua_pushliteral(L, "idle"); 270.365 + break; 270.366 + case PQTRANS_ACTIVE: 270.367 + lua_pushliteral(L, "active"); 270.368 + break; 270.369 + case PQTRANS_INTRANS: 270.370 + lua_pushliteral(L, "intrans"); 270.371 + break; 270.372 + case PQTRANS_INERROR: 270.373 + lua_pushliteral(L, "inerror"); 270.374 + break; 270.375 + default: 270.376 + lua_pushliteral(L, "unknown"); 270.377 + } 270.378 + return 1; 270.379 +} 270.380 + 270.381 +static int mondelefant_conn_create_list(lua_State *L) { 270.382 + lua_settop(L, 2); 270.383 + luaL_checkudata(L, 1, MONDELEFANT_CONN_MT_REGKEY); 270.384 + if (!lua_toboolean(L, 2)) { 270.385 + lua_newtable(L); 270.386 + lua_replace(L, 2); // new result at stack position 2 270.387 + } 270.388 + lua_getfield(L, LUA_REGISTRYINDEX, MONDELEFANT_RESULT_MT_REGKEY); // 3 270.389 + lua_setmetatable(L, 2); 270.390 + lua_pushvalue(L, 1); // 3 270.391 + lua_setfield(L, 2, "_connection"); 270.392 + lua_pushliteral(L, "list"); // 3 270.393 + lua_setfield(L, 2, "_type"); 270.394 + return 1; 270.395 +} 270.396 + 270.397 +static int mondelefant_conn_create_object(lua_State *L) { 270.398 + lua_settop(L, 2); 270.399 + luaL_checkudata(L, 1, MONDELEFANT_CONN_MT_REGKEY); 270.400 + if (!lua_toboolean(L, 2)) { 270.401 + lua_newtable(L); 270.402 + lua_replace(L, 2); // new result at stack position 2 270.403 + } 270.404 + lua_getfield(L, LUA_REGISTRYINDEX, MONDELEFANT_RESULT_MT_REGKEY); // 3 270.405 + lua_setmetatable(L, 2); 270.406 + lua_pushvalue(L, 1); // 3 270.407 + lua_setfield(L, 2, "_connection"); 270.408 + lua_pushliteral(L, "object"); // 3 270.409 + lua_setfield(L, 2, "_type"); // "object" or "list" 270.410 + lua_newtable(L); // 3 270.411 + lua_setfield(L, 2, "_data"); 270.412 + lua_newtable(L); // 3 270.413 + lua_setfield(L, 2, "_dirty"); 270.414 + lua_newtable(L); // 3 270.415 + lua_setfield(L, 2, "_ref"); // nil=no info, false=nil, else table 270.416 + return 1; 270.417 +} 270.418 + 270.419 +static int mondelefant_conn_quote_string(lua_State *L) { 270.420 + mondelefant_conn_t *conn; 270.421 + const char *input; 270.422 + size_t input_len; 270.423 + char *output; 270.424 + size_t output_len; 270.425 + lua_settop(L, 2); 270.426 + conn = mondelefant_get_conn(L, 1); 270.427 + input = luaL_checklstring(L, 2, &input_len); 270.428 + if (input_len > (SIZE_MAX / sizeof(char) - 3) / 2) { 270.429 + return luaL_error(L, "String to be escaped is too long."); 270.430 + } 270.431 + output = malloc((2 * input_len + 3) * sizeof(char)); 270.432 + if (!output) { 270.433 + return luaL_error(L, "Could not allocate memory for string quoting."); 270.434 + } 270.435 + output[0] = '\''; 270.436 + output_len = PQescapeStringConn( 270.437 + conn->pgconn, output + 1, input, input_len, NULL 270.438 + ); 270.439 + output[output_len + 1] = '\''; 270.440 + output[output_len + 2] = 0; 270.441 + lua_pushlstring(L, output, output_len + 2); 270.442 + free(output); 270.443 + return 1; 270.444 +} 270.445 + 270.446 +static int mondelefant_conn_quote_binary(lua_State *L) { 270.447 + mondelefant_conn_t *conn; 270.448 + const char *input; 270.449 + size_t input_len; 270.450 + char *output; 270.451 + size_t output_len; 270.452 + luaL_Buffer buf; 270.453 + lua_settop(L, 2); 270.454 + conn = mondelefant_get_conn(L, 1); 270.455 + input = luaL_checklstring(L, 2, &input_len); 270.456 + output = (char *)PQescapeByteaConn( 270.457 + conn->pgconn, (const unsigned char *)input, input_len, &output_len 270.458 + ); 270.459 + if (!output) { 270.460 + return luaL_error(L, "Could not allocate memory for binary quoting."); 270.461 + } 270.462 + luaL_buffinit(L, &buf); 270.463 + luaL_addchar(&buf, '\''); 270.464 + luaL_addlstring(&buf, output, output_len - 1); 270.465 + luaL_addchar(&buf, '\''); 270.466 + luaL_pushresult(&buf); 270.467 + PQfreemem(output); 270.468 + return 1; 270.469 +} 270.470 + 270.471 +static int mondelefant_conn_assemble_command(lua_State *L) { 270.472 + mondelefant_conn_t *conn; 270.473 + int paramidx = 2; 270.474 + const char *template; 270.475 + size_t template_pos = 0; 270.476 + luaL_Buffer buf; 270.477 + lua_settop(L, 2); 270.478 + conn = mondelefant_get_conn(L, 1); 270.479 + if (lua_isstring(L, 2)) { 270.480 + lua_tostring(L, 2); 270.481 + return 1; 270.482 + } 270.483 + // extra feature for objects with __tostring meta-method: 270.484 + if (luaL_callmeta(L, 2, "__tostring")) return 1; 270.485 + luaL_checktype(L, 2, LUA_TTABLE); 270.486 + lua_rawgeti(L, 2, 1); // 3 270.487 + luaL_argcheck(L, 270.488 + lua_isstring(L, 3), 270.489 + 2, 270.490 + "First entry of SQL command structure is not a string." 270.491 + ); 270.492 + template = lua_tostring(L, 3); 270.493 + lua_pushliteral(L, "input_converter"); // 4 270.494 + lua_gettable(L, 1); // input_converter at stack position 4 270.495 + lua_pushnil(L); // free space at stack position 5 270.496 + lua_pushnil(L); // free space at stack position 6 270.497 + luaL_buffinit(L, &buf); 270.498 + while (1) { 270.499 + char c; 270.500 + c = template[template_pos++]; 270.501 + if (!c) break; 270.502 + if (c == '?' || c == '$') { 270.503 + if (template[template_pos] == c) { 270.504 + template_pos++; 270.505 + luaL_addchar(&buf, c); 270.506 + } else { 270.507 + luaL_Buffer keybuf; 270.508 + int subcmd; 270.509 + subcmd = (c == '$'); 270.510 + luaL_buffinit(L, &keybuf); 270.511 + while (1) { 270.512 + c = template[template_pos]; 270.513 + if ( 270.514 + (c < 'A' || c > 'Z') && 270.515 + (c < 'a' || c > 'z') && 270.516 + (c < '0' || c > '9') && 270.517 + (c != '_') 270.518 + ) break; 270.519 + luaL_addchar(&keybuf, c); 270.520 + template_pos++; 270.521 + } 270.522 + luaL_pushresult(&keybuf); 270.523 + if (lua_objlen(L, -1)) { 270.524 + lua_pushvalue(L, -1); // save key on stack 270.525 + lua_gettable(L, 2); // fetch value 270.526 + } else { 270.527 + lua_pop(L, 1); 270.528 + lua_pushnil(L); // put nil on key position 270.529 + lua_rawgeti(L, 2, paramidx++); // fetch value 270.530 + } 270.531 + // stack: ..., <buffer>, key, pre-value 270.532 + if (subcmd) { 270.533 + size_t i; 270.534 + size_t count; 270.535 + lua_replace(L, 5); // sub-structure at stack position 5 270.536 + lua_pop(L, 1); // drop stored key 270.537 + // stack: ..., <buffer> 270.538 + luaL_argcheck(L, 270.539 + !lua_isnil(L, 5), 270.540 + 2, 270.541 + "SQL sub-structure not found." 270.542 + ); 270.543 + luaL_argcheck(L, 270.544 + lua_type(L, 5) == LUA_TTABLE, 270.545 + 2, 270.546 + "SQL sub-structure must be a table." 270.547 + ); 270.548 + // stack: ..., <buffer> 270.549 + lua_getfield(L, 5, "sep"); 270.550 + lua_replace(L, 6); // seperator at stack position 6 270.551 + if (lua_isnil(L, 6)) { 270.552 + lua_pushstring(L, ", "); 270.553 + lua_replace(L, 6); 270.554 + } else { 270.555 + luaL_argcheck(L, 270.556 + lua_isstring(L, 6), 270.557 + 2, 270.558 + "Seperator of SQL sub-structure has to be a string." 270.559 + ); 270.560 + } 270.561 + count = lua_objlen(L, 5); 270.562 + for (i = 0; i < count; i++) { 270.563 + if (i) { 270.564 + lua_pushvalue(L, 6); 270.565 + luaL_addvalue(&buf); 270.566 + } 270.567 + lua_pushcfunction(L, mondelefant_conn_assemble_command); 270.568 + lua_pushvalue(L, 1); 270.569 + lua_rawgeti(L, 5, i+1); 270.570 + lua_call(L, 2, 1); 270.571 + luaL_addvalue(&buf); 270.572 + } 270.573 + } else { 270.574 + if (lua_toboolean(L, 4)) { 270.575 + // call input_converter with connection handle, value and info 270.576 + lua_pushvalue(L, 4); 270.577 + lua_pushvalue(L, 1); 270.578 + lua_pushvalue(L, -3); 270.579 + lua_newtable(L); 270.580 + lua_pushvalue(L, -6); 270.581 + lua_setfield(L, -2, "field_name"); 270.582 + lua_call(L, 3, 1); 270.583 + // stack: ..., <buffer>, key, pre-value, final-value 270.584 + lua_remove(L, -2); 270.585 + lua_remove(L, -2); 270.586 + // stack: ..., <buffer>, final-value 270.587 + if (!lua_isstring(L, -1)) { 270.588 + return luaL_error(L, "input_converter returned non-string."); 270.589 + } 270.590 + } else { 270.591 + lua_remove(L, -2); 270.592 + // stack: ..., <buffer>, pre-value 270.593 + if (lua_isnil(L, -1)) { 270.594 + lua_pushliteral(L, "NULL"); 270.595 + } else if (lua_type(L, -1) == LUA_TBOOLEAN) { 270.596 + lua_pushstring(L, lua_toboolean(L, -1) ? "TRUE" : "FALSE"); 270.597 + } else if (lua_isstring(L, -1)) { 270.598 + // NOTE: In this version of lua a number will be converted 270.599 + lua_tostring(L, -1); 270.600 + lua_pushcfunction(L, mondelefant_conn_quote_string); 270.601 + lua_pushvalue(L, 1); 270.602 + lua_pushvalue(L, -3); 270.603 + lua_call(L, 2, 1); 270.604 + } else { 270.605 + return luaL_error(L, 270.606 + "Unable to convert SQL value due to unknown type " 270.607 + "or missing input_converter." 270.608 + ); 270.609 + } 270.610 + // stack: ..., <buffer>, pre-value, final-value 270.611 + lua_remove(L, -2); 270.612 + // stack: ..., <buffer>, final-value 270.613 + } 270.614 + luaL_addvalue(&buf); 270.615 + } 270.616 + } 270.617 + } else { 270.618 + luaL_addchar(&buf, c); 270.619 + } 270.620 + } 270.621 + luaL_pushresult(&buf); 270.622 + return 1; 270.623 +} 270.624 + 270.625 +#define MONDELEFANT_MAX_COMMAND_COUNT 64 270.626 +#define MONDELEFANT_MAX_COLUMN_COUNT 1024 270.627 +#define MONDELEFANT_QUERY_MODE_LIST 1 270.628 +#define MONDELEFANT_QUERY_MODE_OBJECT 2 270.629 +#define MONDELEFANT_QUERY_MODE_OPT_OBJECT 3 270.630 + 270.631 +static int mondelefant_conn_try_query(lua_State *L) { 270.632 + mondelefant_conn_t *conn; 270.633 + int command_count; 270.634 + int command_idx; 270.635 + int modes[MONDELEFANT_MAX_COMMAND_COUNT]; 270.636 + luaL_Buffer buf; 270.637 + int sent_success; 270.638 + PGresult *res; 270.639 + int rows, cols, row, col; 270.640 + conn = mondelefant_get_conn(L, 1); 270.641 + command_count = lua_gettop(L) / 2; 270.642 + lua_pushnil(L); // needed, if last mode was omitted 270.643 + if (command_count > MONDELEFANT_MAX_COMMAND_COUNT) { 270.644 + return luaL_error(L, "Exceeded maximum command count in one query."); 270.645 + } 270.646 + luaL_buffinit(L, &buf); 270.647 + for (command_idx = 0; command_idx < command_count; command_idx++) { 270.648 + int mode; 270.649 + int mode_idx; // stack index of mode string 270.650 + if (command_idx) luaL_addchar(&buf, ' '); 270.651 + lua_pushcfunction(L, mondelefant_conn_assemble_command); 270.652 + lua_pushvalue(L, 1); 270.653 + lua_pushvalue(L, 2 + 2 * command_idx); 270.654 + lua_call(L, 2, 1); 270.655 + luaL_addvalue(&buf); 270.656 + luaL_addchar(&buf, ';'); 270.657 + mode_idx = 3 + 2 * command_idx; 270.658 + if (lua_isnil(L, mode_idx)) { 270.659 + mode = MONDELEFANT_QUERY_MODE_LIST; 270.660 + } else { 270.661 + const char *modestr; 270.662 + modestr = luaL_checkstring(L, mode_idx); 270.663 + if (!strcmp(modestr, "list")) { 270.664 + mode = MONDELEFANT_QUERY_MODE_LIST; 270.665 + } else if (!strcmp(modestr, "object")) { 270.666 + mode = MONDELEFANT_QUERY_MODE_OBJECT; 270.667 + } else if (!strcmp(modestr, "opt_object")) { 270.668 + mode = MONDELEFANT_QUERY_MODE_OPT_OBJECT; 270.669 + } else { 270.670 + return luaL_error(L, "Unknown query mode specified."); 270.671 + } 270.672 + } 270.673 + modes[command_idx] = mode; 270.674 + } 270.675 + luaL_pushresult(&buf); // stack position unknown 270.676 + lua_replace(L, 2); // SQL command string to stack position 2 270.677 + lua_settop(L, 2); 270.678 + lua_getfield(L, 1, "sql_tracer"); // tracer at stack position 3 270.679 + if (lua_toboolean(L, 3)) { 270.680 + lua_pushvalue(L, 1); // 4 270.681 + lua_pushvalue(L, 2); // 5 270.682 + lua_call(L, 2, 1); // trace callback at stack position 3 270.683 + } 270.684 + sent_success = PQsendQuery(conn->pgconn, lua_tostring(L, 2)); 270.685 + lua_newtable(L); // results in table at stack position 4 270.686 + for (command_idx = 0; ; command_idx++) { 270.687 + int mode; 270.688 + char binary[MONDELEFANT_MAX_COLUMN_COUNT]; 270.689 + ExecStatusType pgstatus; 270.690 + mode = modes[command_idx]; 270.691 + if (sent_success) { 270.692 + res = PQgetResult(conn->pgconn); 270.693 + if (command_idx >= command_count && !res) break; 270.694 + if (res) { 270.695 + pgstatus = PQresultStatus(res); 270.696 + rows = PQntuples(res); 270.697 + cols = PQnfields(res); 270.698 + } 270.699 + } 270.700 + if ( 270.701 + !sent_success || command_idx >= command_count || !res || 270.702 + (pgstatus != PGRES_TUPLES_OK && pgstatus != PGRES_COMMAND_OK) || 270.703 + (rows < 1 && mode == MONDELEFANT_QUERY_MODE_OBJECT) || 270.704 + (rows > 1 && mode != MONDELEFANT_QUERY_MODE_LIST) 270.705 + ) { 270.706 + const char *command; 270.707 + command = lua_tostring(L, 2); 270.708 + lua_newtable(L); // 5 270.709 + lua_getfield(L, 270.710 + LUA_REGISTRYINDEX, 270.711 + MONDELEFANT_ERROROBJECT_MT_REGKEY 270.712 + ); 270.713 + lua_setmetatable(L, 5); 270.714 + lua_pushvalue(L, 1); 270.715 + lua_setfield(L, 5, "connection"); 270.716 + lua_pushinteger(L, command_idx + 1); 270.717 + lua_setfield(L, 5, "command_number"); 270.718 + lua_pushvalue(L, 2); 270.719 + lua_setfield(L, 5, "sql_command"); 270.720 + if (!res) { 270.721 + lua_pushliteral(L, MONDELEFANT_ERRCODE_RESULTCOUNT_LOW); 270.722 + lua_setfield(L, 5, "code"); 270.723 + lua_pushliteral(L, "Received too few database result sets."); 270.724 + lua_setfield(L, 5, "message"); 270.725 + } else if (command_idx >= command_count) { 270.726 + lua_pushliteral(L, MONDELEFANT_ERRCODE_RESULTCOUNT_HIGH); 270.727 + lua_setfield(L, 5, "code"); 270.728 + lua_pushliteral(L, "Received too many database result sets."); 270.729 + lua_setfield(L, 5, "message"); 270.730 + } else if ( 270.731 + pgstatus != PGRES_TUPLES_OK && pgstatus != PGRES_COMMAND_OK 270.732 + ) { 270.733 + const char *sqlstate; 270.734 + const char *errmsg; 270.735 + lua_pushstring(L, PQresultErrorField(res, PG_DIAG_SEVERITY)); 270.736 + lua_setfield(L, 5, "pg_severity"); 270.737 + sqlstate = PQresultErrorField(res, PG_DIAG_SQLSTATE); 270.738 + if (sqlstate) { 270.739 + lua_pushstring(L, sqlstate); 270.740 + lua_setfield(L, 5, "pg_sqlstate"); 270.741 + lua_pushstring(L, mondelefant_translate_errcode(sqlstate)); 270.742 + lua_setfield(L, 5, "code"); 270.743 + } else { 270.744 + lua_pushliteral(L, MONDELEFANT_ERRCODE_UNKNOWN); 270.745 + lua_setfield(L, 5, "code"); 270.746 + } 270.747 + errmsg = PQresultErrorField(res, PG_DIAG_MESSAGE_PRIMARY); 270.748 + if (errmsg) { 270.749 + mondelefant_push_first_line(L, errmsg); 270.750 + lua_setfield(L, 5, "message"); 270.751 + lua_pushstring(L, errmsg); 270.752 + lua_setfield(L, 5, "pg_message_primary"); 270.753 + } else { 270.754 + lua_pushliteral(L, 270.755 + "Error while fetching result, but no error message given." 270.756 + ); 270.757 + lua_setfield(L, 5, "message"); 270.758 + } 270.759 + lua_pushstring(L, PQresultErrorField(res, PG_DIAG_MESSAGE_DETAIL)); 270.760 + lua_setfield(L, 5, "pg_message_detail"); 270.761 + lua_pushstring(L, PQresultErrorField(res, PG_DIAG_MESSAGE_HINT)); 270.762 + lua_setfield(L, 5, "pg_message_hint"); 270.763 + // NOTE: "position" and "pg_internal_position" are recalculated to 270.764 + // byte offsets, as Lua 5.1 is not Unicode aware. 270.765 + { 270.766 + char *tmp; 270.767 + tmp = PQresultErrorField(res, PG_DIAG_STATEMENT_POSITION); 270.768 + if (tmp) { 270.769 + int pos; 270.770 + pos = atoi(tmp) - 1; 270.771 + if (conn->server_encoding == MONDELEFANT_SERVER_ENCODING_UTF8) { 270.772 + pos = utf8_position_to_byte(command, pos); 270.773 + } 270.774 + lua_pushinteger(L, pos + 1); 270.775 + lua_setfield(L, 5, "position"); 270.776 + } 270.777 + } 270.778 + { 270.779 + const char *internal_query; 270.780 + internal_query = PQresultErrorField(res, PG_DIAG_INTERNAL_QUERY); 270.781 + lua_pushstring(L, internal_query); 270.782 + lua_setfield(L, 5, "pg_internal_query"); 270.783 + char *tmp; 270.784 + tmp = PQresultErrorField(res, PG_DIAG_INTERNAL_POSITION); 270.785 + if (tmp) { 270.786 + int pos; 270.787 + pos = atoi(tmp) - 1; 270.788 + if (conn->server_encoding == MONDELEFANT_SERVER_ENCODING_UTF8) { 270.789 + pos = utf8_position_to_byte(internal_query, pos); 270.790 + } 270.791 + lua_pushinteger(L, pos + 1); 270.792 + lua_setfield(L, 5, "pg_internal_position"); 270.793 + } 270.794 + } 270.795 + lua_pushstring(L, PQresultErrorField(res, PG_DIAG_CONTEXT)); 270.796 + lua_setfield(L, 5, "pg_context"); 270.797 + lua_pushstring(L, PQresultErrorField(res, PG_DIAG_SOURCE_FILE)); 270.798 + lua_setfield(L, 5, "pg_source_file"); 270.799 + lua_pushstring(L, PQresultErrorField(res, PG_DIAG_SOURCE_LINE)); 270.800 + lua_setfield(L, 5, "pg_source_line"); 270.801 + lua_pushstring(L, PQresultErrorField(res, PG_DIAG_SOURCE_FUNCTION)); 270.802 + lua_setfield(L, 5, "pg_source_function"); 270.803 + } else if (rows < 1 && mode == MONDELEFANT_QUERY_MODE_OBJECT) { 270.804 + lua_pushliteral(L, MONDELEFANT_ERRCODE_QUERY1_NO_ROWS); 270.805 + lua_setfield(L, 5, "code"); 270.806 + lua_pushliteral(L, "Expected one row, but got empty set."); 270.807 + lua_setfield(L, 5, "message"); 270.808 + } else if (rows > 1 && mode != MONDELEFANT_QUERY_MODE_LIST) { 270.809 + lua_pushliteral(L, MONDELEFANT_ERRCODE_QUERY1_MULTIPLE_ROWS); 270.810 + lua_setfield(L, 5, "code"); 270.811 + lua_pushliteral(L, "Got more than one result row."); 270.812 + lua_setfield(L, 5, "message"); 270.813 + } else { 270.814 + // should not happen 270.815 + abort(); 270.816 + } 270.817 + if (res) { 270.818 + PQclear(res); 270.819 + while ((res = PQgetResult(conn->pgconn))) PQclear(res); 270.820 + } 270.821 + if (lua_toboolean(L, 3)) { 270.822 + lua_pushvalue(L, 3); 270.823 + lua_pushvalue(L, 5); 270.824 + lua_call(L, 1, 0); 270.825 + } 270.826 + return 1; 270.827 + } 270.828 + rows = PQntuples(res); 270.829 + cols = PQnfields(res); 270.830 + if (modes[command_idx] == MONDELEFANT_QUERY_MODE_LIST) { 270.831 + lua_pushcfunction(L, mondelefant_conn_create_list); // 5 270.832 + lua_pushvalue(L, 1); // 6 270.833 + lua_call(L, 1, 1); // 5 270.834 + } else { 270.835 + lua_pushcfunction(L, mondelefant_conn_create_object); // 5 270.836 + lua_pushvalue(L, 1); // 6 270.837 + lua_call(L, 1, 1); // 5 270.838 + } 270.839 + lua_newtable(L); // column_info at atack position 6 270.840 + for (col = 0; col < cols; col++) { 270.841 + lua_newtable(L); // 7 270.842 + lua_pushstring(L, PQfname(res, col)); 270.843 + lua_setfield(L, 7, "field_name"); 270.844 + { 270.845 + Oid tmp; 270.846 + tmp = PQftable(res, col); 270.847 + if (tmp == InvalidOid) lua_pushnil(L); 270.848 + else lua_pushinteger(L, tmp); 270.849 + lua_setfield(L, 7, "table_oid"); 270.850 + } 270.851 + { 270.852 + int tmp; 270.853 + tmp = PQftablecol(res, col); 270.854 + if (tmp == 0) lua_pushnil(L); 270.855 + else lua_pushinteger(L, tmp); 270.856 + lua_setfield(L, 7, "table_column_number"); 270.857 + } 270.858 + { 270.859 + Oid tmp; 270.860 + tmp = PQftype(res, col); 270.861 + binary[col] = (tmp == MONDELEFANT_POSTGRESQL_BINARY_OID); 270.862 + lua_pushinteger(L, tmp); 270.863 + lua_setfield(L, 7, "type_oid"); 270.864 + lua_pushstring(L, mondelefant_oid_to_typestr(tmp)); 270.865 + lua_setfield(L, 7, "type"); 270.866 + } 270.867 + { 270.868 + int tmp; 270.869 + tmp = PQfmod(res, col); 270.870 + if (tmp == -1) lua_pushnil(L); 270.871 + else lua_pushinteger(L, tmp); 270.872 + lua_setfield(L, 7, "type_modifier"); 270.873 + } 270.874 + lua_rawseti(L, 6, col+1); 270.875 + } 270.876 + lua_setfield(L, 5, "_column_info"); // stack at position 5 with result 270.877 + { 270.878 + char *tmp; 270.879 + tmp = PQcmdTuples(res); 270.880 + if (tmp[0]) { 270.881 + lua_pushinteger(L, atoi(tmp)); 270.882 + lua_setfield(L, 5, "_rows_affected"); 270.883 + } 270.884 + } 270.885 + { 270.886 + Oid tmp; 270.887 + tmp = PQoidValue(res); 270.888 + if (tmp != InvalidOid) { 270.889 + lua_pushinteger(L, tmp); 270.890 + lua_setfield(L, 5, "_oid"); 270.891 + } 270.892 + } 270.893 + if (modes[command_idx] == MONDELEFANT_QUERY_MODE_LIST) { 270.894 + for (row = 0; row < rows; row++) { 270.895 + lua_pushcfunction(L, mondelefant_conn_create_object); // 6 270.896 + lua_pushvalue(L, 1); // 7 270.897 + lua_call(L, 1, 1); // 6 270.898 + for (col = 0; col < cols; col++) { 270.899 + if (PQgetisnull(res, row, col)) { 270.900 + lua_pushnil(L); 270.901 + } else if (binary[col]) { 270.902 + size_t binlen; 270.903 + char *binval; 270.904 + binval = (char *)PQunescapeBytea( 270.905 + (unsigned char *)PQgetvalue(res, row, col), &binlen 270.906 + ); 270.907 + if (!binval) { 270.908 + return luaL_error(L, 270.909 + "Could not allocate memory for binary unescaping." 270.910 + ); 270.911 + } 270.912 + lua_pushlstring(L, binval, binlen); 270.913 + PQfreemem(binval); 270.914 + } else { 270.915 + lua_pushstring(L, PQgetvalue(res, row, col)); 270.916 + } 270.917 + lua_rawseti(L, 6, col+1); 270.918 + } 270.919 + lua_rawseti(L, 5, row+1); 270.920 + } 270.921 + } else if (rows == 1) { 270.922 + for (col = 0; col < cols; col++) { 270.923 + if (PQgetisnull(res, 0, col)) { 270.924 + lua_pushnil(L); 270.925 + } else if (binary[col]) { 270.926 + size_t binlen; 270.927 + char *binval; 270.928 + binval = (char *)PQunescapeBytea( 270.929 + (unsigned char *)PQgetvalue(res, 0, col), &binlen 270.930 + ); 270.931 + if (!binval) { 270.932 + return luaL_error(L, 270.933 + "Could not allocate memory for binary unescaping." 270.934 + ); 270.935 + } 270.936 + lua_pushlstring(L, binval, binlen); 270.937 + PQfreemem(binval); 270.938 + } else { 270.939 + lua_pushstring(L, PQgetvalue(res, 0, col)); 270.940 + } 270.941 + lua_rawseti(L, 5, col+1); 270.942 + } 270.943 + } else { 270.944 + // no row in optrow mode 270.945 + lua_pop(L, 1); 270.946 + lua_pushnil(L); 270.947 + } 270.948 + lua_rawseti(L, 4, command_idx+1); 270.949 + if (lua_gettop(L) != 4) abort(); // should not happen 270.950 + PQclear(res); 270.951 + } 270.952 + // trace callback at stack position 3 270.953 + // result at stack position 4 (top of stack) 270.954 + if (lua_toboolean(L, 3)) { 270.955 + lua_pushvalue(L, 3); 270.956 + lua_call(L, 0, 0); 270.957 + } 270.958 + lua_replace(L, 3); // result at stack position 3 270.959 + lua_getfield(L, 1, "output_converter"); // output converter at stack position 4 270.960 + for (command_idx = 0; command_idx < command_count; command_idx++) { 270.961 + int mode; 270.962 + mode = modes[command_idx]; 270.963 + lua_rawgeti(L, 3, command_idx+1); // raw result at stack position 5 270.964 + if (lua_toboolean(L, 5)) { 270.965 + lua_getfield(L, 5, "_column_info"); // column_info list at position 6 270.966 + cols = lua_objlen(L, 6); 270.967 + if (mode == MONDELEFANT_QUERY_MODE_LIST) { 270.968 + rows = lua_objlen(L, 5); 270.969 + for (row = 0; row < rows; row++) { 270.970 + lua_rawgeti(L, 5, row+1); // row at stack position 7 270.971 + lua_getfield(L, 7, "_data"); // _data table at stack position 8 270.972 + for (col = 0; col < cols; col++) { 270.973 + lua_rawgeti(L, 6, col+1); // this column info at position 9 270.974 + lua_getfield(L, 9, "field_name"); // 10 270.975 + if (lua_toboolean(L, 4)) { 270.976 + lua_pushvalue(L, 4); // output-converter 270.977 + lua_pushvalue(L, 1); // connection 270.978 + lua_rawgeti(L, 7, col+1); // raw-value 270.979 + lua_pushvalue(L, 9); // this column info 270.980 + lua_call(L, 3, 1); // converted value at position 11 270.981 + } else { 270.982 + lua_rawgeti(L, 7, col+1); // raw-value at position 11 270.983 + } 270.984 + lua_pushvalue(L, 11); // 12 270.985 + lua_rawseti(L, 7, col+1); 270.986 + lua_rawset(L, 8); 270.987 + lua_settop(L, 8); 270.988 + } 270.989 + lua_settop(L, 6); 270.990 + } 270.991 + } else { 270.992 + lua_getfield(L, 5, "_data"); // _data table at stack position 7 270.993 + for (col = 0; col < cols; col++) { 270.994 + lua_rawgeti(L, 6, col+1); // this column info at position 8 270.995 + lua_getfield(L, 8, "field_name"); // 9 270.996 + if (lua_toboolean(L, 4)) { 270.997 + lua_pushvalue(L, 4); // output-converter 270.998 + lua_pushvalue(L, 1); // connection 270.999 + lua_rawgeti(L, 5, col+1); // raw-value 270.1000 + lua_pushvalue(L, 8); // this column info 270.1001 + lua_call(L, 3, 1); // converted value at position 10 270.1002 + } else { 270.1003 + lua_rawgeti(L, 5, col+1); // raw-value at position 10 270.1004 + } 270.1005 + lua_pushvalue(L, 10); // 11 270.1006 + lua_rawseti(L, 5, col+1); 270.1007 + lua_rawset(L, 7); 270.1008 + lua_settop(L, 7); 270.1009 + } 270.1010 + } 270.1011 + } 270.1012 + lua_settop(L, 4); 270.1013 + } 270.1014 + lua_settop(L, 3); 270.1015 + lua_pushnil(L); 270.1016 + for (command_idx = 0; command_idx < command_count; command_idx++) { 270.1017 + lua_rawgeti(L, 3, command_idx+1); 270.1018 + } 270.1019 + return command_count+1; 270.1020 +} 270.1021 + 270.1022 +static int mondelefant_errorobject_escalate(lua_State *L) { 270.1023 + lua_settop(L, 1); 270.1024 + lua_getfield(L, 1, "connection"); // 2 270.1025 + lua_getfield(L, 2, "error_objects"); // 3 270.1026 + if (lua_toboolean(L, 3)) { 270.1027 + lua_settop(L, 1); 270.1028 + return lua_error(L); 270.1029 + } else { 270.1030 + lua_getfield(L, 1, "message"); // 4 270.1031 + if (lua_isnil(L, 4)) { 270.1032 + return luaL_error(L, "No error message given for escalation."); 270.1033 + } 270.1034 + return lua_error(L); 270.1035 + } 270.1036 +} 270.1037 + 270.1038 +static int mondelefant_errorobject_is_kind_of(lua_State *L) { 270.1039 + lua_settop(L, 2); 270.1040 + lua_getfield(L, 1, "code"); // 3 270.1041 + if (lua_isstring(L, 3)) { 270.1042 + lua_pushboolean(L, 270.1043 + mondelefant_check_error_class( 270.1044 + lua_tostring(L, 3), luaL_checkstring(L, 2) 270.1045 + ) 270.1046 + ); 270.1047 + } else { 270.1048 + // only happens for errors where code is not set 270.1049 + lua_pushboolean(L, 0); 270.1050 + } 270.1051 + return 1; 270.1052 +} 270.1053 + 270.1054 +static int mondelefant_conn_query(lua_State *L) { 270.1055 + int argc; 270.1056 + argc = lua_gettop(L); 270.1057 + lua_pushvalue(L, 1); 270.1058 + lua_insert(L, 1); 270.1059 + lua_pushcfunction(L, mondelefant_conn_try_query); 270.1060 + lua_insert(L, 2); 270.1061 + lua_call(L, argc, LUA_MULTRET); // results (with error) starting at index 2 270.1062 + if (lua_toboolean(L, 2)) { 270.1063 + lua_pushcfunction(L, mondelefant_errorobject_escalate); 270.1064 + lua_pushvalue(L, 2); 270.1065 + lua_call(L, 1, 0); // will raise an error 270.1066 + return 0; // should not be executed 270.1067 + } else { 270.1068 + return lua_gettop(L) - 2; 270.1069 + } 270.1070 +} 270.1071 + 270.1072 +static int mondelefant_set_class(lua_State *L) { 270.1073 + lua_settop(L, 2); 270.1074 + lua_getmetatable(L, 1); // 3 270.1075 + lua_getfield(L, LUA_REGISTRYINDEX, MONDELEFANT_RESULT_MT_REGKEY); // 4 270.1076 + luaL_argcheck(L, lua_equal(L, 3, 4), 1, "not a database result"); 270.1077 + lua_settop(L, 2); 270.1078 + lua_getmetatable(L, 2); // 3 270.1079 + lua_getfield(L, LUA_REGISTRYINDEX, MONDELEFANT_CLASS_MT_REGKEY); // 4 270.1080 + luaL_argcheck(L, lua_equal(L, 3, 4), 2, "not a database class"); 270.1081 + lua_settop(L, 2); 270.1082 + lua_pushvalue(L, 2); // 3 270.1083 + lua_setfield(L, 1, "_class"); 270.1084 + lua_getfield(L, 1, "_type"); // 3 270.1085 + lua_pushliteral(L, "list"); // 4 270.1086 + if (lua_rawequal(L, 3, 4)) { 270.1087 + int i; 270.1088 + for (i=0; i < lua_objlen(L, 1); i++) { 270.1089 + lua_settop(L, 2); 270.1090 + lua_rawgeti(L, 1, i+1); // 3 270.1091 + lua_pushvalue(L, 2); // 4 270.1092 + lua_setfield(L, 3, "_class"); 270.1093 + } 270.1094 + } 270.1095 + lua_settop(L, 1); 270.1096 + return 1; 270.1097 +} 270.1098 + 270.1099 +static int mondelefant_new_class(lua_State *L) { 270.1100 + lua_settop(L, 1); 270.1101 + if (!lua_toboolean(L, 1)) { 270.1102 + lua_settop(L, 0); 270.1103 + lua_newtable(L); // 1 270.1104 + } 270.1105 + lua_getfield(L, LUA_REGISTRYINDEX, MONDELEFANT_CLASS_MT_REGKEY); // 2 270.1106 + lua_setmetatable(L, 1); 270.1107 + lua_pushliteral(L, "prototype"); // 2 270.1108 + lua_rawget(L, 1); // 2 270.1109 + if (!lua_toboolean(L, 2)) { 270.1110 + lua_pushliteral(L, "prototype"); // 3 270.1111 + lua_getfield(L, 270.1112 + LUA_REGISTRYINDEX, 270.1113 + MONDELEFANT_CLASS_PROTO_REGKEY 270.1114 + ); // 4 270.1115 + lua_rawset(L, 1); 270.1116 + } 270.1117 + lua_settop(L, 1); 270.1118 + lua_pushliteral(L, "object"); // 2 270.1119 + lua_rawget(L, 1); // 2 270.1120 + if (!lua_toboolean(L, 2)) { 270.1121 + lua_pushliteral(L, "object"); // 3 270.1122 + lua_newtable(L); // 4 270.1123 + lua_rawset(L, 1); 270.1124 + } 270.1125 + lua_settop(L, 1); 270.1126 + lua_pushliteral(L, "object_get"); // 2 270.1127 + lua_rawget(L, 1); // 2 270.1128 + if (!lua_toboolean(L, 2)) { 270.1129 + lua_pushliteral(L, "object_get"); // 3 270.1130 + lua_newtable(L); // 4 270.1131 + lua_rawset(L, 1); 270.1132 + } 270.1133 + lua_settop(L, 1); 270.1134 + lua_pushliteral(L, "object_set"); // 2 270.1135 + lua_rawget(L, 1); // 2 270.1136 + if (!lua_toboolean(L, 2)) { 270.1137 + lua_pushliteral(L, "object_set"); // 3 270.1138 + lua_newtable(L); // 4 270.1139 + lua_rawset(L, 1); 270.1140 + } 270.1141 + lua_settop(L, 1); 270.1142 + lua_pushliteral(L, "list"); // 2 270.1143 + lua_rawget(L, 1); // 2 270.1144 + if (!lua_toboolean(L, 2)) { 270.1145 + lua_pushliteral(L, "list"); // 3 270.1146 + lua_newtable(L); // 4 270.1147 + lua_rawset(L, 1); 270.1148 + } 270.1149 + lua_settop(L, 1); 270.1150 + lua_pushliteral(L, "references"); // 2 270.1151 + lua_rawget(L, 1); // 2 270.1152 + if (!lua_toboolean(L, 2)) { 270.1153 + lua_pushliteral(L, "references"); // 3 270.1154 + lua_newtable(L); // 4 270.1155 + lua_rawset(L, 1); 270.1156 + } 270.1157 + lua_settop(L, 1); 270.1158 + lua_pushliteral(L, "foreign_keys"); // 2 270.1159 + lua_rawget(L, 1); // 2 270.1160 + if (!lua_toboolean(L, 2)) { 270.1161 + lua_pushliteral(L, "foreign_keys"); // 3 270.1162 + lua_newtable(L); // 4 270.1163 + lua_rawset(L, 1); 270.1164 + } 270.1165 + lua_settop(L, 1); 270.1166 + return 1; 270.1167 +} 270.1168 + 270.1169 +static int mondelefant_class_get_reference(lua_State *L) { 270.1170 + lua_settop(L, 2); 270.1171 + while (lua_toboolean(L, 1)) { 270.1172 + lua_getfield(L, 1, "references"); // 3 270.1173 + lua_pushvalue(L, 2); // 4 270.1174 + lua_gettable(L, 3); // 4 270.1175 + if (!lua_isnil(L, 4)) return 1; 270.1176 + lua_settop(L, 2); 270.1177 + lua_pushliteral(L, "prototype"); // 3 270.1178 + lua_rawget(L, 1); // 3 270.1179 + lua_replace(L, 1); 270.1180 + } 270.1181 + return 0; 270.1182 +} 270.1183 + 270.1184 +static int mondelefant_class_iterate_over_references(lua_State *L) { 270.1185 + return luaL_error(L, "Reference iterator not implemented yet."); // TODO 270.1186 +} 270.1187 + 270.1188 +static int mondelefant_class_get_foreign_key_reference_name(lua_State *L) { 270.1189 + lua_settop(L, 2); 270.1190 + while (lua_toboolean(L, 1)) { 270.1191 + lua_getfield(L, 1, "foreign_keys"); // 3 270.1192 + lua_pushvalue(L, 2); // 4 270.1193 + lua_gettable(L, 3); // 4 270.1194 + if (!lua_isnil(L, 4)) return 1; 270.1195 + lua_settop(L, 2); 270.1196 + lua_pushliteral(L, "prototype"); // 3 270.1197 + lua_rawget(L, 1); // 3 270.1198 + lua_replace(L, 1); 270.1199 + } 270.1200 + return 0; 270.1201 +} 270.1202 + 270.1203 +static int mondelefant_result_index(lua_State *L) { 270.1204 + const char *result_type; 270.1205 + lua_settop(L, 2); 270.1206 + if (lua_type(L, 2) != LUA_TSTRING || lua_tostring(L, 2)[0] == '_') { 270.1207 + lua_rawget(L, 1); 270.1208 + return 1; 270.1209 + } 270.1210 + lua_getfield(L, 1, "_class"); // 3 270.1211 + if (!lua_toboolean(L, 3)) { 270.1212 + lua_settop(L, 2); 270.1213 + lua_getfield(L, 270.1214 + LUA_REGISTRYINDEX, 270.1215 + MONDELEFANT_CLASS_PROTO_REGKEY 270.1216 + ); // 3 270.1217 + } 270.1218 + lua_getfield(L, 1, "_type"); // 4 270.1219 + result_type = lua_tostring(L, 4); 270.1220 + if (result_type && !strcmp(result_type, "object")) { 270.1221 + lua_settop(L, 3); 270.1222 + // try inherited attributes, methods or getter functions: 270.1223 + while (lua_toboolean(L, 3)) { 270.1224 + lua_getfield(L, 3, "object"); // 4 270.1225 + lua_pushvalue(L, 2); // 5 270.1226 + lua_gettable(L, 4); // 5 270.1227 + if (!lua_isnil(L, 5)) return 1; 270.1228 + lua_settop(L, 3); 270.1229 + lua_getfield(L, 3, "object_get"); // 4 270.1230 + lua_pushvalue(L, 2); // 5 270.1231 + lua_gettable(L, 4); // 5 270.1232 + if (lua_toboolean(L, 5)) { 270.1233 + lua_pushvalue(L, 1); // 6 270.1234 + lua_call(L, 1, 1); // 5 270.1235 + return 1; 270.1236 + } 270.1237 + lua_settop(L, 3); 270.1238 + lua_pushliteral(L, "prototype"); // 4 270.1239 + lua_rawget(L, 3); // 4 270.1240 + lua_replace(L, 3); 270.1241 + } 270.1242 + lua_settop(L, 2); 270.1243 + // try primary keys of referenced objects: 270.1244 + lua_pushcfunction(L, 270.1245 + mondelefant_class_get_foreign_key_reference_name 270.1246 + ); // 3 270.1247 + lua_getfield(L, 1, "_class"); // 4 270.1248 + lua_pushvalue(L, 2); // 5 270.1249 + lua_call(L, 2, 1); // 3 270.1250 + if (!lua_isnil(L, 3)) { 270.1251 + // reference name at stack position 3 270.1252 + lua_pushcfunction(L, mondelefant_class_get_reference); // 4 270.1253 + lua_getfield(L, 1, "_class"); // 5 270.1254 + lua_pushvalue(L, 3); // 6 270.1255 + lua_call(L, 2, 1); // reference info at stack position 4 270.1256 + lua_getfield(L, 1, "_ref"); // 5 270.1257 + lua_getfield(L, 4, "ref"); // 6 270.1258 + lua_gettable(L, 5); // 6 270.1259 + if (!lua_isnil(L, 6)) { 270.1260 + if (lua_toboolean(L, 6)) { 270.1261 + lua_getfield(L, 4, "that_key"); // 7 270.1262 + if (lua_isnil(L, 7)) { 270.1263 + return luaL_error(L, "Missing 'that_key' entry in model reference."); 270.1264 + } 270.1265 + lua_gettable(L, 6); // 7 270.1266 + } else { 270.1267 + lua_pushnil(L); 270.1268 + } 270.1269 + return 1; 270.1270 + } 270.1271 + } 270.1272 + lua_settop(L, 2); 270.1273 + // try normal data field info: 270.1274 + lua_getfield(L, 1, "_data"); // 3 270.1275 + lua_pushvalue(L, 2); // 4 270.1276 + lua_gettable(L, 3); // 4 270.1277 + if (!lua_isnil(L, 4)) return 1; 270.1278 + lua_settop(L, 2); 270.1279 + // try cached referenced object (or cached NULL reference): 270.1280 + lua_getfield(L, 1, "_ref"); // 3 270.1281 + lua_pushvalue(L, 2); // 4 270.1282 + lua_gettable(L, 3); // 4 270.1283 + if (lua_isboolean(L, 4) && !lua_toboolean(L, 4)) { 270.1284 + lua_pushnil(L); 270.1285 + return 1; 270.1286 + } else if (!lua_isnil(L, 4)) { 270.1287 + return 1; 270.1288 + } 270.1289 + lua_settop(L, 2); 270.1290 + // try to load a referenced object: 270.1291 + lua_pushcfunction(L, mondelefant_class_get_reference); // 3 270.1292 + lua_getfield(L, 1, "_class"); // 4 270.1293 + lua_pushvalue(L, 2); // 5 270.1294 + lua_call(L, 2, 1); // 3 270.1295 + if (!lua_isnil(L, 3)) { 270.1296 + lua_settop(L, 2); 270.1297 + lua_getfield(L, 1, "load"); // 3 270.1298 + lua_pushvalue(L, 1); // 4 (self) 270.1299 + lua_pushvalue(L, 2); // 5 270.1300 + lua_call(L, 2, 0); 270.1301 + lua_settop(L, 2); 270.1302 + lua_getfield(L, 1, "_ref"); // 3 270.1303 + lua_pushvalue(L, 2); // 4 270.1304 + lua_gettable(L, 3); // 4 270.1305 + if (lua_isboolean(L, 4) && !lua_toboolean(L, 4)) lua_pushnil(L); // TODO: use special object instead of false 270.1306 + return 1; 270.1307 + } 270.1308 + return 0; 270.1309 + } else if (result_type && !strcmp(result_type, "list")) { 270.1310 + lua_settop(L, 3); 270.1311 + // try inherited list attributes or methods: 270.1312 + while (lua_toboolean(L, 3)) { 270.1313 + lua_getfield(L, 3, "list"); // 4 270.1314 + lua_pushvalue(L, 2); // 5 270.1315 + lua_gettable(L, 4); // 5 270.1316 + if (!lua_isnil(L, 5)) return 1; 270.1317 + lua_settop(L, 3); 270.1318 + lua_pushliteral(L, "prototype"); // 4 270.1319 + lua_rawget(L, 3); // 4 270.1320 + lua_replace(L, 3); 270.1321 + } 270.1322 + } 270.1323 + return 0; 270.1324 +} 270.1325 + 270.1326 +static int mondelefant_result_newindex(lua_State *L) { 270.1327 + const char *result_type; 270.1328 + lua_settop(L, 3); 270.1329 + if (lua_type(L, 2) != LUA_TSTRING || lua_tostring(L, 2)[0] == '_') { 270.1330 + lua_rawset(L, 1); 270.1331 + return 1; 270.1332 + } 270.1333 + lua_getfield(L, 1, "_class"); // 4 270.1334 + if (!lua_toboolean(L, 4)) { 270.1335 + lua_settop(L, 3); 270.1336 + lua_getfield(L, 270.1337 + LUA_REGISTRYINDEX, 270.1338 + MONDELEFANT_CLASS_PROTO_REGKEY 270.1339 + ); // 4 270.1340 + } 270.1341 + lua_getfield(L, 1, "_type"); // 5 270.1342 + result_type = lua_tostring(L, 5); 270.1343 + if (result_type && !strcmp(result_type, "object")) { 270.1344 + lua_settop(L, 4); 270.1345 + // try object setter functions: 270.1346 + while (lua_toboolean(L, 4)) { 270.1347 + lua_getfield(L, 4, "object_set"); // 5 270.1348 + lua_pushvalue(L, 2); // 6 270.1349 + lua_gettable(L, 5); // 6 270.1350 + if (lua_toboolean(L, 6)) { 270.1351 + lua_pushvalue(L, 1); // 7 270.1352 + lua_pushvalue(L, 3); // 8 270.1353 + lua_call(L, 2, 0); 270.1354 + return 0; 270.1355 + } 270.1356 + lua_settop(L, 4); 270.1357 + lua_pushliteral(L, "prototype"); // 5 270.1358 + lua_rawget(L, 4); // 5 270.1359 + lua_replace(L, 4); 270.1360 + } 270.1361 + lua_settop(L, 3); 270.1362 + // check, if a object reference is changed: 270.1363 + lua_pushcfunction(L, mondelefant_class_get_reference); // 4 270.1364 + lua_getfield(L, 1, "_class"); // 5 270.1365 + lua_pushvalue(L, 2); // 6 270.1366 + lua_call(L, 2, 1); // 4 270.1367 + if (!lua_isnil(L, 4)) { 270.1368 + // store object in _ref table (use false for nil): // TODO: use special object instead of false 270.1369 + lua_getfield(L, 1, "_ref"); // 5 270.1370 + lua_pushvalue(L, 2); // 6 270.1371 + if (lua_isnil(L, 3)) lua_pushboolean(L, 0); // 7 270.1372 + else lua_pushvalue(L, 3); // 7 270.1373 + lua_settable(L, 5); 270.1374 + lua_settop(L, 4); 270.1375 + // delete referencing key from _data table: 270.1376 + lua_getfield(L, 4, "this_key"); // 5 270.1377 + if (lua_isnil(L, 5)) { 270.1378 + return luaL_error(L, "Missing 'this_key' entry in model reference."); 270.1379 + } 270.1380 + lua_getfield(L, 1, "_data"); // 6 270.1381 + lua_pushvalue(L, 5); // 7 270.1382 + lua_pushnil(L); // 8 270.1383 + lua_settable(L, 6); 270.1384 + lua_settop(L, 5); 270.1385 + lua_getfield(L, 1, "_dirty"); // 6 270.1386 + lua_pushvalue(L, 5); // 7 270.1387 + lua_pushboolean(L, 1); // 8 270.1388 + lua_settable(L, 6); 270.1389 + return 0; 270.1390 + } 270.1391 + lua_settop(L, 3); 270.1392 + // store value in data field info: 270.1393 + lua_getfield(L, 1, "_data"); // 4 270.1394 + lua_pushvalue(L, 2); // 5 270.1395 + lua_pushvalue(L, 3); // 6 270.1396 + lua_settable(L, 4); 270.1397 + lua_settop(L, 3); 270.1398 + // mark field as dirty (needs to be UPDATEd on save): 270.1399 + lua_getfield(L, 1, "_dirty"); // 4 270.1400 + lua_pushvalue(L, 2); // 5 270.1401 + lua_pushboolean(L, 1); // 6 270.1402 + lua_settable(L, 4); 270.1403 + lua_settop(L, 3); 270.1404 + // reset reference cache, if neccessary: 270.1405 + lua_pushcfunction(L, 270.1406 + mondelefant_class_get_foreign_key_reference_name 270.1407 + ); // 4 270.1408 + lua_getfield(L, 1, "_class"); // 5 270.1409 + lua_pushvalue(L, 2); // 6 270.1410 + lua_call(L, 2, 1); // 4 270.1411 + if (!lua_isnil(L, 4)) { 270.1412 + lua_getfield(L, 1, "_ref"); // 5 270.1413 + lua_pushvalue(L, 4); // 6 270.1414 + lua_pushnil(L); // 7 270.1415 + lua_settable(L, 5); 270.1416 + } 270.1417 + return 0; 270.1418 + } else { 270.1419 + lua_settop(L, 3); 270.1420 + lua_rawset(L, 1); 270.1421 + return 0; 270.1422 + } 270.1423 + return 0; 270.1424 +} 270.1425 + 270.1426 +static int mondelefant_class_index(lua_State *L) { 270.1427 + lua_settop(L, 2); 270.1428 + lua_pushliteral(L, "prototype"); // 3 270.1429 + lua_rawget(L, 1); // 3 270.1430 + lua_pushvalue(L, 2); // 4 270.1431 + lua_gettable(L, 3); // 4 270.1432 + return 1; 270.1433 +} 270.1434 + 270.1435 +static const struct luaL_Reg mondelefant_module_functions[] = { 270.1436 + {"connect", mondelefant_connect}, 270.1437 + {"set_class", mondelefant_set_class}, 270.1438 + {"new_class", mondelefant_new_class}, 270.1439 + {NULL, NULL} 270.1440 +}; 270.1441 + 270.1442 +static const struct luaL_Reg mondelefant_conn_mt_functions[] = { 270.1443 + {"__gc", mondelefant_conn_free}, 270.1444 + {"__index", mondelefant_conn_index}, 270.1445 + {"__newindex", mondelefant_conn_newindex}, 270.1446 + {NULL, NULL} 270.1447 +}; 270.1448 + 270.1449 +static const struct luaL_Reg mondelefant_conn_methods[] = { 270.1450 + {"close", mondelefant_conn_close}, 270.1451 + {"is_ok", mondelefant_conn_is_ok}, 270.1452 + {"get_transaction_status", mondelefant_conn_get_transaction_status}, 270.1453 + {"create_list", mondelefant_conn_create_list}, 270.1454 + {"create_object", mondelefant_conn_create_object}, 270.1455 + {"quote_string", mondelefant_conn_quote_string}, 270.1456 + {"quote_binary", mondelefant_conn_quote_binary}, 270.1457 + {"assemble_command", mondelefant_conn_assemble_command}, 270.1458 + {"try_query", mondelefant_conn_try_query}, 270.1459 + {"query", mondelefant_conn_query}, 270.1460 + {NULL, NULL} 270.1461 +}; 270.1462 + 270.1463 +static const struct luaL_Reg mondelefant_errorobject_mt_functions[] = { 270.1464 + {NULL, NULL} 270.1465 +}; 270.1466 + 270.1467 +static const struct luaL_Reg mondelefant_errorobject_methods[] = { 270.1468 + {"escalate", mondelefant_errorobject_escalate}, 270.1469 + {"is_kind_of", mondelefant_errorobject_is_kind_of}, 270.1470 + {NULL, NULL} 270.1471 +}; 270.1472 + 270.1473 +static const struct luaL_Reg mondelefant_result_mt_functions[] = { 270.1474 + {"__index", mondelefant_result_index}, 270.1475 + {"__newindex", mondelefant_result_newindex}, 270.1476 + {NULL, NULL} 270.1477 +}; 270.1478 + 270.1479 +static const struct luaL_Reg mondelefant_class_mt_functions[] = { 270.1480 + {"__index", mondelefant_class_index}, 270.1481 + {NULL, NULL} 270.1482 +}; 270.1483 + 270.1484 +static const struct luaL_Reg mondelefant_class_methods[] = { 270.1485 + {"get_reference", mondelefant_class_get_reference}, 270.1486 + {"iterate_over_references", mondelefant_class_iterate_over_references}, 270.1487 + {"get_foreign_key_reference_name", 270.1488 + mondelefant_class_get_foreign_key_reference_name}, 270.1489 + {NULL, NULL} 270.1490 +}; 270.1491 + 270.1492 +static const struct luaL_Reg mondelefant_object_methods[] = { 270.1493 + {NULL, NULL} 270.1494 +}; 270.1495 + 270.1496 +static const struct luaL_Reg mondelefant_list_methods[] = { 270.1497 + {NULL, NULL} 270.1498 +}; 270.1499 + 270.1500 +int luaopen_mondelefant_native(lua_State *L) { 270.1501 + lua_settop(L, 0); 270.1502 + lua_newtable(L); // module at stack position 1 270.1503 + luaL_register(L, NULL, mondelefant_module_functions); 270.1504 + 270.1505 + lua_pushvalue(L, 1); // 2 270.1506 + lua_setfield(L, LUA_REGISTRYINDEX, MONDELEFANT_MODULE_REGKEY); 270.1507 + 270.1508 + lua_newtable(L); // 2 270.1509 + // NOTE: only PostgreSQL is supported yet: 270.1510 + luaL_register(L, NULL, mondelefant_conn_methods); 270.1511 + lua_setfield(L, 1, "postgresql_connection_prototype"); 270.1512 + lua_newtable(L); // 2 270.1513 + lua_setfield(L, 1, "connection_prototype"); 270.1514 + 270.1515 + luaL_newmetatable(L, MONDELEFANT_CONN_MT_REGKEY); // 2 270.1516 + luaL_register(L, NULL, mondelefant_conn_mt_functions); 270.1517 + lua_settop(L, 1); 270.1518 + luaL_newmetatable(L, MONDELEFANT_RESULT_MT_REGKEY); // 2 270.1519 + luaL_register(L, NULL, mondelefant_result_mt_functions); 270.1520 + lua_setfield(L, 1, "result_metatable"); 270.1521 + luaL_newmetatable(L, MONDELEFANT_CLASS_MT_REGKEY); // 2 270.1522 + luaL_register(L, NULL, mondelefant_class_mt_functions); 270.1523 + lua_setfield(L, 1, "class_metatable"); 270.1524 + 270.1525 + lua_newtable(L); // 2 270.1526 + lua_newtable(L); // 3 270.1527 + luaL_register(L, NULL, mondelefant_object_methods); 270.1528 + lua_setfield(L, 2, "object"); 270.1529 + lua_newtable(L); // 3 270.1530 + lua_setfield(L, 2, "object_get"); 270.1531 + lua_newtable(L); // 3 270.1532 + lua_setfield(L, 2, "object_set"); 270.1533 + lua_newtable(L); // 3 270.1534 + luaL_register(L, NULL, mondelefant_list_methods); 270.1535 + lua_setfield(L, 2, "list"); 270.1536 + lua_newtable(L); // 3 270.1537 + lua_setfield(L, 2, "references"); 270.1538 + lua_newtable(L); // 3 270.1539 + lua_setfield(L, 2, "foreign_keys"); 270.1540 + lua_pushvalue(L, 2); // 3 270.1541 + lua_setfield(L, LUA_REGISTRYINDEX, MONDELEFANT_CLASS_PROTO_REGKEY); 270.1542 + lua_setfield(L, 1, "class_prototype"); 270.1543 + 270.1544 + lua_newtable(L); // 2 270.1545 + lua_pushliteral(L, "k"); // 3 270.1546 + lua_setfield(L, 2, "__mode"); 270.1547 + lua_newtable(L); // 3 270.1548 + lua_pushvalue(L, 2); // 4 270.1549 + lua_setmetatable(L, 3); 270.1550 + lua_setfield(L, LUA_REGISTRYINDEX, MONDELEFANT_CONN_DATA_REGKEY); 270.1551 + lua_settop(L, 1); 270.1552 + 270.1553 + luaL_newmetatable(L, MONDELEFANT_ERROROBJECT_MT_REGKEY); // 2 270.1554 + luaL_register(L, NULL, mondelefant_errorobject_mt_functions); 270.1555 + lua_newtable(L); // 3 270.1556 + luaL_register(L, NULL, mondelefant_errorobject_methods); 270.1557 + lua_setfield(L, 2, "__index"); 270.1558 + lua_setfield(L, 1, "errorobject_metatable"); 270.1559 + 270.1560 + return 1; 270.1561 +}
271.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 271.2 +++ b/libraries/multirand/Makefile Sun Oct 25 12:00:00 2009 +0100 271.3 @@ -0,0 +1,10 @@ 271.4 +include ../../Makefile.options 271.5 + 271.6 +multirand.so: multirand.o 271.7 + $(LD) $(LDFLAGS) -o multirand.$(SLIB_EXT) multirand.o 271.8 + 271.9 +multirand.o: multirand.c 271.10 + $(CC) -c $(CFLAGS) -o multirand.o multirand.c 271.11 + 271.12 +clean:: 271.13 + rm -f multirand.so multirand.o
272.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 272.2 +++ b/libraries/multirand/multirand.c Sun Oct 25 12:00:00 2009 +0100 272.3 @@ -0,0 +1,124 @@ 272.4 +#include <lua.h> 272.5 +#include <lauxlib.h> 272.6 +#include <dirent.h> 272.7 + 272.8 +static FILE *multirand_dev; 272.9 + 272.10 +static lua_Integer multirand_range( 272.11 + lua_State *L, lua_Integer from, lua_Integer to 272.12 +) { 272.13 + lua_Integer range; 272.14 + int bits = 0; 272.15 + lua_Integer bit_mask = 0; 272.16 + if (to < from) return luaL_error(L, "Assertion failed in C."); 272.17 + range = to - from; 272.18 + { 272.19 + lua_Integer tmp; 272.20 + tmp = range; 272.21 + while (tmp) { 272.22 + bits++; 272.23 + bit_mask <<= 1; 272.24 + bit_mask |= 1; 272.25 + tmp >>= 1; 272.26 + } 272.27 + } 272.28 + while (1) { 272.29 + int i; 272.30 + lua_Integer rnd = 0; 272.31 + for (i = 0; i < (bits + 7) / 8; i++) { 272.32 + int b; 272.33 + b = getc(multirand_dev); 272.34 + if (b == EOF) { 272.35 + return luaL_error(L, "I/O error while reading random."); 272.36 + } 272.37 + rnd = (rnd << 8) | (unsigned char)b; 272.38 + } 272.39 + rnd &= bit_mask; 272.40 + if (rnd <= range) return from + rnd; 272.41 + } 272.42 +} 272.43 + 272.44 +static int multirand_integer(lua_State *L) { 272.45 + lua_Integer arg1, arg2; 272.46 + lua_settop(L, 2); 272.47 + arg1 = luaL_checkinteger(L, 1); 272.48 + if (lua_toboolean(L, 2)) { 272.49 + arg2 = luaL_optinteger(L, 2, 0); 272.50 + if (arg1 > arg2) { 272.51 + return luaL_error(L, 272.52 + "Upper boundary is smaller than lower boundary." 272.53 + ); 272.54 + } else if (arg1 == arg2) { 272.55 + lua_pushinteger(L, arg1); 272.56 + } else { 272.57 + lua_pushinteger(L, multirand_range(L, arg1, arg2)); 272.58 + } 272.59 + } else { 272.60 + luaL_argcheck(L, arg1 >= 1, 1, "smaller than 1"); 272.61 + lua_pushinteger(L, multirand_range(L, 1, arg1)); 272.62 + } 272.63 + return 1; 272.64 +} 272.65 + 272.66 +static int multirand_fraction(lua_State *L) { 272.67 + int i, j; 272.68 + lua_settop(L, 0); 272.69 + lua_Number rnd = 0.0; 272.70 + for (i = 0; i < 8; i++) { 272.71 + int b; 272.72 + unsigned char c; 272.73 + b = getc(multirand_dev); 272.74 + if (b == EOF) return luaL_error(L, "I/O error while reading random."); 272.75 + c = (unsigned char) b; 272.76 + for (j = 0; j < 8; j++) { 272.77 + rnd /= 2.0; 272.78 + if (c & 1) rnd += 0.5; 272.79 + c >>= 1; 272.80 + } 272.81 + } 272.82 + lua_pushnumber(L, rnd); 272.83 + return 1; 272.84 +} 272.85 + 272.86 +static int multirand_string(lua_State *L) { 272.87 + int length; 272.88 + const char *charset; 272.89 + size_t charset_size; 272.90 + luaL_Buffer buf; 272.91 + lua_settop(L, 2); 272.92 + length = luaL_checkint(L, 1); 272.93 + charset = luaL_optlstring(L, 2, "abcdefghijklmnopqrstuvwxyz", &charset_size); 272.94 + if (charset_size > 32767) { 272.95 + return luaL_error(L, "Set of chars is too big."); 272.96 + } 272.97 + luaL_buffinit(L, &buf); 272.98 + for (; length > 0; length--) { 272.99 + luaL_addchar(&buf, 272.100 + charset[multirand_range(L, 0, (lua_Integer)charset_size - 1)] 272.101 + ); 272.102 + } 272.103 + luaL_pushresult(&buf); 272.104 + return 1; 272.105 +} 272.106 + 272.107 +static const struct luaL_Reg multirand_module_functions[] = { 272.108 + {"integer", multirand_integer}, 272.109 + {"fraction", multirand_fraction}, 272.110 + {"string", multirand_string}, 272.111 + {NULL, NULL} 272.112 +}; 272.113 + 272.114 +int luaopen_multirand(lua_State *L) { 272.115 + const char *module_name; 272.116 + lua_settop(L, 1); 272.117 + module_name = lua_tostring(L, 1); 272.118 + if (module_name) { 272.119 + luaL_register(L, module_name, multirand_module_functions); 272.120 + } else { 272.121 + luaL_register(L, "multirand", multirand_module_functions); 272.122 + } 272.123 + lua_replace(L, 1); 272.124 + multirand_dev = fopen("/dev/urandom", "r"); 272.125 + if (!multirand_dev) return luaL_error(L, "Could not open /dev/urandom."); 272.126 + return 1; 272.127 +}
273.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 273.2 +++ b/libraries/nihil/nihil.lua Sun Oct 25 12:00:00 2009 +0100 273.3 @@ -0,0 +1,52 @@ 273.4 +#!/usr/bin/env lua 273.5 + 273.6 +local error = error 273.7 +local getmetatable = getmetatable 273.8 +local module = module 273.9 +local rawset = rawset 273.10 +local setmetatable = setmetatable 273.11 + 273.12 +module(...) 273.13 + 273.14 +metatable = { 273.15 + __tostring = function(self) 273.16 + return "nil" .. self[1] 273.17 + end, 273.18 + __newindex = function() 273.19 + error("Objects representing nil are immutable.") 273.20 + end 273.21 +} 273.22 + 273.23 +nils = setmetatable({}, { 273.24 + __mode = "v", 273.25 + __index = function(self, level) 273.26 + if level > 0 then 273.27 + local result = setmetatable({ level }, metatable) 273.28 + rawset(self, level, result) 273.29 + return result 273.30 + end 273.31 + end, 273.32 + __newindex = function() 273.33 + error("Table is immutable.") 273.34 + end 273.35 +}) 273.36 + 273.37 +function lift(value) 273.38 + if value == nil then 273.39 + return nils[1] 273.40 + elseif getmetatable(value) == metatable then 273.41 + return nils[value[1]+1] 273.42 + else 273.43 + return value 273.44 + end 273.45 +end 273.46 + 273.47 +function lower(value) 273.48 + if value == nil then 273.49 + error("Cannot lower nil.") 273.50 + elseif getmetatable(value) == metatable then 273.51 + return nils[value[1]-1] 273.52 + else 273.53 + return value 273.54 + end 273.55 +end
274.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 274.2 +++ b/libraries/rocketcgi/rocketcgi.lua Sun Oct 25 12:00:00 2009 +0100 274.3 @@ -0,0 +1,219 @@ 274.4 +#!/usr/bin/env lua 274.5 + 274.6 +local assert = assert 274.7 +local error = error 274.8 +local getfenv = getfenv 274.9 +local getmetatable = getmetatable 274.10 +local ipairs = ipairs 274.11 +local next = next 274.12 +local pairs = pairs 274.13 +local pcall = pcall 274.14 +local print = print 274.15 +local rawequal = rawequal 274.16 +local rawget = rawget 274.17 +local rawset = rawset 274.18 +local select = select 274.19 +local setfenv = setfenv 274.20 +local setmetatable = setmetatable 274.21 +local tonumber = tonumber 274.22 +local tostring = tostring 274.23 +local type = type 274.24 +local unpack = unpack 274.25 +local xpcall = xpcall 274.26 + 274.27 +local io = io 274.28 +local math = math 274.29 +local os = os 274.30 +local string = string 274.31 +local table = table 274.32 + 274.33 +module(...) 274.34 + 274.35 +data_sent = false 274.36 + 274.37 +function add_header(...) 274.38 + if data_sent then 274.39 + error("Can not add header after data has been sent.", 2) 274.40 + end 274.41 + io.stdout:write(...) 274.42 + io.stdout:write("\r\n") 274.43 +end 274.44 + 274.45 +function send_data(...) 274.46 + if not data_sent then 274.47 + io.stdout:write("\r\n") 274.48 + data_sent = true 274.49 + end 274.50 + io.stdout:write(...) 274.51 +end 274.52 + 274.53 +function set_status(status) 274.54 + add_header("Status: ", status) 274.55 +end 274.56 + 274.57 +function redirect(location) 274.58 + set_status("303 See Other") 274.59 + add_header("Location: ", location) 274.60 +end 274.61 + 274.62 +function set_content_type(content_type) 274.63 + add_header("Content-Type: ", content_type) 274.64 +end 274.65 + 274.66 +method = os.getenv("REQUEST_METHOD") or false 274.67 +query = os.getenv("QUERY_STRING") or false 274.68 +cookie_data = os.getenv("HTTP_COOKIE") or false 274.69 +post_data = io.stdin:read("*a") or false 274.70 +post_contenttype = os.getenv("CONTENT_TYPE") or false 274.71 +params = {} 274.72 +get_params = {} 274.73 +cookies = {} 274.74 +post_params = {} 274.75 +post_filenames = {} 274.76 +post_types = {} 274.77 + 274.78 +local urldecode 274.79 +do 274.80 + local b0 = string.byte("0") 274.81 + local b9 = string.byte("9") 274.82 + local bA = string.byte("A") 274.83 + local bF = string.byte("F") 274.84 + local ba = string.byte("a") 274.85 + local bf = string.byte("f") 274.86 + function urldecode(str) 274.87 + return ( 274.88 + string.gsub( 274.89 + string.gsub(str, "%+", " "), 274.90 + "%%([0-9A-Fa-f][0-9A-Fa-f])", 274.91 + function(hex) 274.92 + local n1, n2 = string.byte(hex, 1, 2) 274.93 + if n1 >= b0 and n1 <= b9 then n1 = n1 - b0 274.94 + elseif n1 >= bA and n1 <= bF then n1 = n1 - bA + 10 274.95 + elseif n1 >= ba and n1 <= bf then n1 = n1 - ba + 10 274.96 + else return end 274.97 + if n2 >= b0 and n2 <= b9 then n2 = n2 - b0 274.98 + elseif n2 >= bA and n2 <= bF then n2 = n2 - bA + 10 274.99 + elseif n2 >= ba and n2 <= bf then n2 = n2 - ba + 10 274.100 + else return end 274.101 + return string.char(n1 * 16 + n2) 274.102 + end 274.103 + ) 274.104 + ) 274.105 + end 274.106 +end 274.107 + 274.108 +local function proc_param(tbl, key, value) 274.109 + if string.find(key, "%[%]$") then 274.110 + if tbl[key] then 274.111 + table.insert(tbl[key], value) 274.112 + else 274.113 + local list = { value } 274.114 + params[key] = list 274.115 + tbl[key] = list 274.116 + end 274.117 + else 274.118 + params[key] = value 274.119 + tbl[key] = value 274.120 + end 274.121 +end 274.122 + 274.123 +local function read_urlencoded_form(tbl, data) 274.124 + for rawkey, rawvalue in string.gmatch(data, "([^=&]*)=([^=&]*)") do 274.125 + proc_param(tbl, urldecode(rawkey), urldecode(rawvalue)) 274.126 + end 274.127 +end 274.128 + 274.129 +if query then 274.130 + read_urlencoded_form(get_params, query) 274.131 +end 274.132 + 274.133 +if cookie_data then 274.134 + for rawkey, rawvalue in string.gmatch(cookie_data, "([^=; ]*)=([^=; ]*)") do 274.135 + cookies[urldecode(rawkey)] = urldecode(rawvalue) 274.136 + end 274.137 +end 274.138 + 274.139 +if post_contenttype == "application/x-www-form-urlencoded" then 274.140 + read_urlencoded_form(post_params, post_data) 274.141 +elseif post_contenttype then 274.142 + local boundary = string.match( 274.143 + post_contenttype, 274.144 + '^multipart/form%-data[ \t]*;[ \t]*boundary="([^"]+)"' 274.145 + ) or string.match( 274.146 + post_contenttype, 274.147 + '^multipart/form%-data[ \t]*;[ \t]*boundary=([^"; \t]+)' 274.148 + ) 274.149 + if boundary then 274.150 + local parts = {} 274.151 + do 274.152 + local boundary = "\r\n--" .. boundary 274.153 + local post_data = "\r\n" .. post_data 274.154 + local pos1, pos2 = string.find(post_data, boundary, 1, true) 274.155 + while true do 274.156 + local ind = string.sub(post_data, pos2 + 1, pos2 + 2) 274.157 + if ind == "\r\n" then 274.158 + local pos3, pos4 = string.find(post_data, boundary, pos2 + 1, true) 274.159 + if pos3 then 274.160 + parts[#parts + 1] = string.sub(post_data, pos2 + 3, pos3 - 1) 274.161 + else 274.162 + error("Illegal POST data.") 274.163 + end 274.164 + pos1, pos2 = pos3, pos4 274.165 + elseif ind == "--" then 274.166 + break 274.167 + else 274.168 + error("Illegal POST data.") 274.169 + end 274.170 + end 274.171 + end 274.172 + for i, part in ipairs(parts) do 274.173 + local pos = 1 274.174 + local name, filename, contenttype 274.175 + while true do 274.176 + local header 274.177 + do 274.178 + local oldpos = pos 274.179 + pos = string.find(part, "\r\n", oldpos, true) 274.180 + if not pos then 274.181 + error("Illegal POST data.") 274.182 + end 274.183 + if pos == oldpos then break end 274.184 + header = string.sub(part, oldpos, pos - 1) 274.185 + pos = pos + 2 274.186 + end 274.187 + if string.find( 274.188 + string.lower(header), 274.189 + "^content%-disposition:[ \t]*form%-data[ \t]*;" 274.190 + ) then 274.191 + -- TODO: handle all cases correctly 274.192 + name = string.match(header, ';[ \t]*name="([^"]*)"') or 274.193 + string.match(header, ';[ \t]*name=([^"; \t]+)') 274.194 + filename = string.match(header, ';[ \t]*filename="([^"]*)"') or 274.195 + string.match(header, ';[ \t]*filename=([^"; \t]+)') 274.196 + else 274.197 + local dummy, subpos = string.find( 274.198 + string.lower(header), 274.199 + "^content%-type:[ \t]*" 274.200 + ) 274.201 + if subpos then 274.202 + contenttype = string.sub(header, subpos + 1, #header) 274.203 + end 274.204 + end 274.205 + end 274.206 + local content = string.sub(part, pos + 2, #part) 274.207 + if not name then 274.208 + error("Illegal POST data.") 274.209 + end 274.210 + proc_param(post_params, name, content) 274.211 + post_filenames[name] = filename 274.212 + post_types[name] = contenttype 274.213 + end 274.214 + end 274.215 +end 274.216 + 274.217 +if post_data and #post_data > 262144 then 274.218 + post_data = nil 274.219 + collectgarbage("collect") 274.220 +else 274.221 + post_data = nil 274.222 +end