# HG changeset patch
# User jbe/bsw
# Date 1256468400 -3600
# Node ID 9fdfb27f8e676922467f3f14af94dd05427bf60c
Version 1.0.0
diff -r 000000000000 -r 9fdfb27f8e67 LICENSE
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/LICENSE Sun Oct 25 12:00:00 2009 +0100
@@ -0,0 +1,19 @@
+Copyright (c) 2009 Public Software Group e. V., Berlin
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff -r 000000000000 -r 9fdfb27f8e67 Makefile
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Makefile Sun Oct 25 12:00:00 2009 +0100
@@ -0,0 +1,53 @@
+include Makefile.options
+
+all::
+ make documentation
+ make accelerator
+ make libraries
+ make symlinks
+ make precompile
+
+documentation::
+ rm -f doc/autodoc.tmp
+ lua framework/bin/autodoc.lua framework/cgi-bin/ framework/env/ libraries/ > doc/autodoc.tmp
+ cat doc/autodoc-header.htmlpart doc/autodoc.tmp doc/autodoc-footer.htmlpart > doc/autodoc.html
+ rm -f doc/autodoc.tmp
+
+accelerator::
+ cd framework/accelerator; make
+
+libraries::
+ cd libraries/extos; make
+ cd libraries/mondelefant; make
+ cd libraries/multirand; make
+
+symlinks::
+ ln -s -f ../../libraries/atom/atom.lua framework/lib/
+ ln -s -f ../../libraries/extos/extos.so framework/lib/
+ ln -s -f ../../libraries/mondelefant/mondelefant.lua framework/lib/
+ ln -s -f ../../libraries/mondelefant/mondelefant_native.so framework/lib/
+ ln -s -f ../../libraries/mondelefant/mondelefant_atom_connector.lua framework/lib/
+ ln -s -f ../../libraries/multirand/multirand.so framework/lib/
+ ln -s -f ../../libraries/rocketcgi/rocketcgi.lua framework/lib/
+ ln -s -f ../../libraries/nihil/nihil.lua framework/lib/
+ ln -s -f ../../libraries/luatex/luatex.lua framework/lib/
+
+precompile::
+ rm -Rf framework.precompiled
+ rm -Rf demo-app.precompiled
+ sh framework/bin/recursive-luac framework/ framework.precompiled/
+ rm -f framework.precompiled/accelerator/Makefile
+ rm -f framework.precompiled/accelerator/webmcp_accelerator.c
+ rm -f framework.precompiled/accelerator/webmcp_accelerator.o
+ framework/bin/recursive-luac demo-app/ demo-app.precompiled/
+
+clean::
+ rm -f doc/autodoc.tmp doc/autodoc.html
+ rm -Rf framework.precompiled
+ rm -Rf demo-app.precompiled
+ rm -f demo-app/tmp/*
+ rm -f framework/lib/*
+ cd libraries/extos; make clean
+ cd libraries/mondelefant; make clean
+ cd libraries/multirand; make clean
+ cd framework/accelerator; make clean
diff -r 000000000000 -r 9fdfb27f8e67 Makefile.options
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Makefile.options Sun Oct 25 12:00:00 2009 +0100
@@ -0,0 +1,20 @@
+# C compiler command
+CC = cc
+
+# linker command
+LD = ld
+
+# filename extension for shared libraries
+SLIB_EXT = so
+
+# C compiler flags
+CFLAGS = -O2 -Wall -I /usr/include -I /usr/local/include -I /usr/local/include/lua51 -I /usr/include/lua5.1
+
+# additional C compiler flags for parts which depend on PostgreSQL
+CFLAGS_PGSQL = -I /usr/include/postgresql -I /usr/include/postgresql/server -I /usr/local/include/postgresql -I /usr/local/include/postgresql/server
+
+# linker flags
+LDFLAGS = -shared -L /usr/lib -L /usr/local/lib -L /usr/local/lib/lua51 -L /usr/lib/lua5.1
+
+# additional linker flags for parts which depend on PostgreSQL
+LDFLAGS_PGSQL =
diff -r 000000000000 -r 9fdfb27f8e67 demo-app/app/main/_filter/20_session.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/demo-app/app/main/_filter/20_session.lua Sun Oct 25 12:00:00 2009 +0100
@@ -0,0 +1,19 @@
+if cgi.cookies.session then
+ app.session = Session:by_ident(cgi.cookies.session)
+end
+if not app.session then
+ app.session = Session:new()
+ cgi.add_header('Set-Cookie: session=' .. app.session.ident .. '; path=/' )
+end
+
+request.set_csrf_secret(app.session.csrf_secret)
+
+if app.session.user then
+ locale.set{ lang = app.session.user.lang or "en" }
+end
+
+if param.get("lang") then
+ locale.set{ lang = param.get("lang") }
+end
+
+execute.inner()
diff -r 000000000000 -r 9fdfb27f8e67 demo-app/app/main/_filter/21_auth.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/demo-app/app/main/_filter/21_auth.lua Sun Oct 25 12:00:00 2009 +0100
@@ -0,0 +1,25 @@
+local auth_needed = not (
+ request.get_module() == 'index'
+ and (
+ request.get_view() == 'login'
+ or request.get_action() == 'login'
+ )
+)
+
+-- if not app.session.user_id then
+-- trace.debug("DEBUG: AUTHENTICATION BYPASS ENABLED")
+-- app.session.user_id = 1
+-- end
+
+if app.session.user == nil and auth_needed then
+ trace.debug("Not authenticated yet.")
+ request.redirect{ module = 'index', view = 'login' }
+else
+ if auth_needed then
+ trace.debug("Authentication accepted.")
+ else
+ trace.debug("No authentication needed.")
+ end
+ execute.inner()
+ trace.debug("End of authentication filter.")
+end
diff -r 000000000000 -r 9fdfb27f8e67 demo-app/app/main/_filter_action/23_write_priv.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/demo-app/app/main/_filter_action/23_write_priv.lua Sun Oct 25 12:00:00 2009 +0100
@@ -0,0 +1,7 @@
+if
+ not (request.get_module() == "index" and request.get_action() == "login")
+ and not (request.get_module() == "index" and request.get_action() == "logout")
+then
+ app.session.user:require_privilege("write")
+end
+execute.inner()
diff -r 000000000000 -r 9fdfb27f8e67 demo-app/app/main/_filter_view/30_topnav.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/demo-app/app/main/_filter_view/30_topnav.lua Sun Oct 25 12:00:00 2009 +0100
@@ -0,0 +1,83 @@
+-- display navigation only, if user is logged in
+if app.session.user_id == nil then
+ execute.inner()
+ return
+end
+
+slot.select("topnav", function()
+ ui.link{
+ attr = { class = "nav" },
+ text = _"Home",
+ module = "index",
+ view = "index"
+ }
+ ui.link{
+ attr = { class = "nav" },
+ text = _"Media",
+ module = "medium"
+ }
+ ui.link{
+ attr = { class = "nav" },
+ text = _"Media types",
+ module = "media_type"
+ }
+ ui.link{
+ attr = { class = "nav" },
+ text = _"Genres",
+ module = "genre"
+ }
+ if app.session.user.admin then
+ ui.link{
+ attr = { class = "nav" },
+ text = _"Users",
+ module = "user"
+ }
+ end
+ ui.container{
+ attr = { class = "nav lang_chooser" },
+ content = function()
+ for i, lang in ipairs{"en", "de", "es"} do
+ ui.container{
+ content = function()
+ ui.link{
+ content = function()
+ ui.image{
+ static = "lang/" .. lang .. ".png",
+ attr = { alt = lang }
+ }
+ slot.put(lang)
+ end,
+ module = "index",
+ action = "set_lang",
+ params = { lang = lang },
+ routing = {
+ default = {
+ mode = "redirect",
+ module = request.get_module(),
+ view = request.get_view(),
+ id = param.get_id_cgi(),
+ params = param.get_all_cgi()
+ }
+ }
+ }
+ end
+ }
+ end
+ end
+ }
+
+ ui.link{
+ attr = { class = "nav" },
+ text = _"Logout",
+ module = "index",
+ action = "logout",
+ redirect_to = {
+ ok = {
+ module = "index",
+ view = "login"
+ }
+ }
+ }
+end)
+
+execute.inner()
diff -r 000000000000 -r 9fdfb27f8e67 demo-app/app/main/_layout/default.html
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/demo-app/app/main/_layout/default.html Sun Oct 25 12:00:00 2009 +0100
@@ -0,0 +1,58 @@
+
+
+
+ WebMCP Demo Application
+
+
+
+
+
+
+
Demo
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff -r 000000000000 -r 9fdfb27f8e67 demo-app/app/main/_layout/system_error.html
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/demo-app/app/main/_layout/system_error.html Sun Oct 25 12:00:00 2009 +0100
@@ -0,0 +1,41 @@
+
+
+
+ webmcp demo application
+
+
+
+
+
+
+
+
+
+ System message
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
index
+
+
+
+
+
+
\ No newline at end of file
diff -r 000000000000 -r 9fdfb27f8e67 demo-app/app/main/genre/_action/update.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/demo-app/app/main/genre/_action/update.lua Sun Oct 25 12:00:00 2009 +0100
@@ -0,0 +1,24 @@
+local genre
+local id = param.get_id()
+if id then
+ genre = Genre:by_id(id)
+else
+ genre = Genre:new()
+end
+
+if param.get("delete", atom.boolean) then
+ local name = genre.name
+ genre:destroy()
+ slot.put_into("notice", _("Genre '#{name}' deleted", {name = name}))
+ return
+end
+
+param.update(genre, "name", "description")
+
+genre:save()
+
+if id then
+ slot.put_into("notice", _("Genre '#{name}' updated", {name = genre.name}))
+else
+ slot.put_into("notice", _("Genre '#{name}' created", {name = genre.name}))
+end
diff -r 000000000000 -r 9fdfb27f8e67 demo-app/app/main/genre/index.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/demo-app/app/main/genre/index.lua Sun Oct 25 12:00:00 2009 +0100
@@ -0,0 +1,46 @@
+slot.put_into("title", encode.html(_"Genres"))
+
+slot.select("actions", function()
+ if app.session.user.write_priv then
+ ui.link{
+ content = _"Create new genre",
+ module = "genre",
+ view = "show"
+ }
+ end
+end)
+
+
+local selector = Genre:new_selector():add_order_by('"name", "id"')
+
+slot.select("main", function()
+ ui.paginate{
+ selector = selector,
+ content = function()
+ ui.list{
+ records = selector:exec(),
+ columns = {
+ {
+ field_attr = { style = "float: right;" },
+ label = _"Id",
+ name = "id"
+ },
+ {
+ label = _"Name",
+ name = "name"
+ },
+ {
+ content = function(record)
+ ui.link{
+ content = _"Show",
+ module = "genre",
+ view = "show",
+ id = record.id
+ }
+ end
+ },
+ }
+ }
+ end
+ }
+end)
diff -r 000000000000 -r 9fdfb27f8e67 demo-app/app/main/genre/show.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/demo-app/app/main/genre/show.lua Sun Oct 25 12:00:00 2009 +0100
@@ -0,0 +1,63 @@
+local genre
+local id = param.get_id()
+if id then
+ genre = Genre:by_id(id)
+end
+
+if genre then
+ slot.put_into("title", encode.html(_"Genre"))
+else
+ slot.put_into("title", encode.html(_"New genre"))
+end
+
+slot.select("actions", function()
+ ui.link{
+ content = _"Back",
+ module = "genre"
+ }
+ if genre and app.session.user.write_priv then
+ ui.link{
+ content = _"Delete",
+ form_attr = {
+ onsubmit = "return confirm('" .. _'Are you sure?' .. "');"
+ },
+ module = "genre",
+ action = "update",
+ id = genre.id,
+ params = { delete = true },
+ routing = {
+ default = {
+ mode = "redirect",
+ module = "genre",
+ view = "index"
+ }
+ }
+ }
+ end
+end)
+
+slot.select("main", function()
+ ui.form{
+ attr = { class = "vertical" },
+ record = genre,
+ readonly = not app.session.user.write_priv,
+ module = "genre",
+ action = "update",
+ id = id,
+ routing = {
+ default = {
+ mode = "redirect",
+ module = "genre",
+ view = "index"
+ }
+ },
+ content = function()
+ if id then
+ ui.field.integer{ label = _"Id", name = "id", readonly = true }
+ end
+ ui.field.text{ label = _"Name", name = "name" }
+ ui.field.text{ label = _"Description", name = "description", multiline = true }
+ ui.submit{ text = _"Save" }
+ end
+ }
+end)
diff -r 000000000000 -r 9fdfb27f8e67 demo-app/app/main/index/_action/login.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/demo-app/app/main/index/_action/login.lua Sun Oct 25 12:00:00 2009 +0100
@@ -0,0 +1,12 @@
+local user = User:by_ident_and_password(param.get('ident'), param.get('password'))
+
+if user then
+ app.session.user = user
+ app.session:save()
+ slot.put_into('notice', _'Login successful!')
+ trace.debug('User authenticated')
+else
+ slot.put_into('error', _'Invalid username or password!')
+ trace.debug('User NOT authenticated')
+ return false
+end
diff -r 000000000000 -r 9fdfb27f8e67 demo-app/app/main/index/_action/logout.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/demo-app/app/main/index/_action/logout.lua Sun Oct 25 12:00:00 2009 +0100
@@ -0,0 +1,4 @@
+if app.session then
+ app.session:destroy()
+ slot.put_into("notice", _"Logout successful")
+end
diff -r 000000000000 -r 9fdfb27f8e67 demo-app/app/main/index/_action/set_lang.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/demo-app/app/main/index/_action/set_lang.lua Sun Oct 25 12:00:00 2009 +0100
@@ -0,0 +1,6 @@
+app.session.user.lang = param.get("lang")
+app.session.user:save()
+
+locale.set{ lang = app.session.user.lang }
+
+slot.put_into("notice", _"Language changed")
\ No newline at end of file
diff -r 000000000000 -r 9fdfb27f8e67 demo-app/app/main/index/index.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/demo-app/app/main/index/index.lua Sun Oct 25 12:00:00 2009 +0100
@@ -0,0 +1,3 @@
+slot.put_into('title', encode.html(_"webmcp demo application"))
+
+slot.put_into('main', encode.html(_"Welcome to webmcp demo application"))
diff -r 000000000000 -r 9fdfb27f8e67 demo-app/app/main/index/login.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/demo-app/app/main/index/login.lua Sun Oct 25 12:00:00 2009 +0100
@@ -0,0 +1,47 @@
+slot.put_into("title", encode.html(_"Password login"))
+
+slot.select("main", function()
+
+ ui.form{
+ attr = { class = "vertical" },
+ module = "index",
+ action = "login",
+ routing = {
+ default = {
+ mode = "redirect",
+ module = "index",
+ view = "index"
+ }
+ },
+ content = function()
+
+ ui.container{
+ attr = { class = "lang_chooser" },
+ content = function()
+ for i, lang in ipairs{"en", "de", "es"} do
+ ui.container{
+ content = function()
+ ui.link{
+ content = function()
+ ui.image{
+ static = "lang/" .. lang .. ".png",
+ attr = { alt = lang }
+ }
+ slot.put(lang)
+ end,
+ module = "index",
+ view = "login",
+ params = { lang = lang }
+ }
+ end
+ }
+ end
+ end
+ }
+
+ ui.field.text{ label = _"Username", name = "ident" }
+ ui.field.text{ label = _"Password", name = "password" }
+ ui.submit{ text = _"Login" }
+ end
+ }
+end)
\ No newline at end of file
diff -r 000000000000 -r 9fdfb27f8e67 demo-app/app/main/media_type/_action/update.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/demo-app/app/main/media_type/_action/update.lua Sun Oct 25 12:00:00 2009 +0100
@@ -0,0 +1,24 @@
+local media_type
+local id = param.get_id()
+if id then
+ media_type = MediaType:by_id(id)
+else
+ media_type = MediaType:new()
+end
+
+if param.get("delete", atom.boolean) then
+ local name = media_type.name
+ media_type:destroy()
+ slot.put_into("notice", _("Media type '#{name}' deleted", {name = name}))
+ return
+end
+
+param.update(media_type, "name", "description")
+
+media_type:save()
+
+if id then
+ slot.put_into("notice", _("Media type '#{name}' updated", {name = media_type.name}))
+else
+ slot.put_into("notice", _("Media type '#{name}' created", {name = media_type.name}))
+end
diff -r 000000000000 -r 9fdfb27f8e67 demo-app/app/main/media_type/index.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/demo-app/app/main/media_type/index.lua Sun Oct 25 12:00:00 2009 +0100
@@ -0,0 +1,46 @@
+slot.put_into("title", encode.html(_"Media types"))
+
+slot.select("actions", function()
+ if app.session.user.write_priv then
+ ui.link{
+ content = _"Create new media type",
+ module = "media_type",
+ view = "show"
+ }
+ end
+end)
+
+
+local selector = MediaType:new_selector():add_order_by('"name", "id"')
+
+slot.select("main", function()
+ ui.paginate{
+ selector = selector,
+ content = function()
+ ui.list{
+ records = selector:exec(),
+ columns = {
+ {
+ field_attr = { style = "float: right;" },
+ label = _"Id",
+ name = "id"
+ },
+ {
+ label = _"Name",
+ name = "name"
+ },
+ {
+ content = function(record)
+ ui.link{
+ content = _"Show",
+ module = "media_type",
+ view = "show",
+ id = record.id
+ }
+ end
+ },
+ }
+ }
+ end
+ }
+end)
diff -r 000000000000 -r 9fdfb27f8e67 demo-app/app/main/media_type/show.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/demo-app/app/main/media_type/show.lua Sun Oct 25 12:00:00 2009 +0100
@@ -0,0 +1,63 @@
+local media_type
+local id = param.get_id()
+if id then
+ media_type = MediaType:by_id(id)
+end
+
+if media_type then
+ slot.put_into("title", encode.html(_"Media type"))
+else
+ slot.put_into("title", encode.html(_"New media type"))
+end
+
+slot.select("actions", function()
+ ui.link{
+ content = _"Back",
+ module = "media_type"
+ }
+ if media_type and app.session.user.write_priv then
+ ui.link{
+ content = _"Delete",
+ form_attr = {
+ onsubmit = "return confirm('" .. _'Are you sure?' .. "');"
+ },
+ module = "media_type",
+ action = "update",
+ id = media_type.id,
+ params = { delete = true },
+ routing = {
+ default = {
+ mode = "redirect",
+ module = "media_type",
+ view = "index"
+ }
+ }
+ }
+ end
+end)
+
+slot.select("main", function()
+ ui.form{
+ attr = { class = "vertical" },
+ record = media_type,
+ readonly = not app.session.user.write_priv,
+ module = "media_type",
+ action = "update",
+ id = id,
+ routing = {
+ default = {
+ mode = "redirect",
+ module = "media_type",
+ view = "index"
+ }
+ },
+ content = function()
+ if id then
+ ui.field.integer{ label = _"Id", name = "id", readonly = true }
+ end
+ ui.field.text{ label = _"Name", name = "name" }
+ ui.field.text{ label = _"Description", name = "description", multiline = true }
+ ui.submit{ text = _"Save" }
+ end
+ }
+end)
diff -r 000000000000 -r 9fdfb27f8e67 demo-app/app/main/medium/_action/update.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/demo-app/app/main/medium/_action/update.lua Sun Oct 25 12:00:00 2009 +0100
@@ -0,0 +1,51 @@
+local medium
+local id = param.get_id()
+if id then
+ medium = Medium:by_id(id)
+else
+ medium = Medium:new()
+end
+
+if param.get("delete", atom.boolean) then
+ local name = medium.name
+ medium:destroy()
+ slot.put_into("notice", _("Medium '#{name}' deleted", {name = name}))
+ return
+end
+
+param.update(medium, "media_type_id", "name", "copyprotected")
+
+medium:save()
+
+param.update_relationship{
+ param_name = "genres",
+ id = medium.id,
+ connecting_model = Classification,
+ own_reference = "medium_id",
+ foreign_reference = "genre_id"
+}
+
+for index, prefix in param.iterate("tracks") do
+ local id = param.get(prefix .. "id", atom.integer)
+ local track
+ if id then
+ track = Track:by_id(id)
+ elseif #param.get(prefix .. "name") > 0 then
+ track = Track:new()
+ track.medium_id = medium.id
+ else
+ break
+ end
+ track.position = param.get(prefix .. "position", atom.integer)
+ track.name = param.get(prefix .. "name")
+ track.description = param.get(prefix .. "description")
+ track.duration = param.get(prefix .. "duration")
+ track:save()
+end
+
+
+if id then
+ slot.put_into("notice", _("Medium '#{name}' updated", {name = medium.name}))
+else
+ slot.put_into("notice", _("Medium '#{name}' created", {name = medium.name}))
+end
diff -r 000000000000 -r 9fdfb27f8e67 demo-app/app/main/medium/index.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/demo-app/app/main/medium/index.lua Sun Oct 25 12:00:00 2009 +0100
@@ -0,0 +1,50 @@
+slot.put_into("title", encode.html(_"Media"))
+
+slot.select("actions", function()
+ if app.session.user.write_priv then
+ ui.link{
+ content = _"Create new medium",
+ module = "medium",
+ view = "show"
+ }
+ end
+end)
+
+
+local selector = Medium:new_selector():add_order_by('"name", "id"')
+
+slot.select("main", function()
+ ui.paginate{
+ selector = selector,
+ content = function()
+ ui.list{
+ records = selector:exec(),
+ columns = {
+ {
+ field_attr = { style = "float: right;" },
+ label = _"Id",
+ name = "id"
+ },
+ {
+ label = _"Name",
+ name = "name"
+ },
+ {
+ label = _"Copy protected",
+ name = "copyprotected"
+ },
+ {
+ content = function(record)
+ ui.link{
+ content = _"Show",
+ module = "medium",
+ view = "show",
+ id = record.id
+ }
+ end
+ },
+ }
+ }
+ end
+ }
+end)
\ No newline at end of file
diff -r 000000000000 -r 9fdfb27f8e67 demo-app/app/main/medium/show.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/demo-app/app/main/medium/show.lua Sun Oct 25 12:00:00 2009 +0100
@@ -0,0 +1,117 @@
+local medium
+local id = param.get_id()
+if id then
+ medium = Medium:by_id(id)
+end
+
+if medium then
+ slot.put_into("title", encode.html(_"Medium"))
+else
+ slot.put_into("title", encode.html(_"New medium"))
+end
+
+slot.select("actions", function()
+ ui.link{
+ content = _"Back",
+ module = "medium"
+ }
+ if medium and app.session.user.write_priv then
+ ui.link{
+ content = _"Delete",
+ form_attr = {
+ onsubmit = "return confirm(" .. encode.json(_'Are you sure?') .. ");"
+ },
+ module = "medium",
+ action = "update",
+ id = medium.id,
+ params = { delete = true },
+ routing = {
+ default = {
+ mode = "redirect",
+ module = "medium",
+ view = "index"
+ }
+ }
+ }
+ end
+end)
+
+slot.select("main", function()
+ ui.form{
+ attr = { class = "vertical" },
+ record = medium,
+ readonly = not app.session.user.write_priv,
+ module = "medium",
+ action = "update",
+ id = id,
+ routing = {
+ default = {
+ mode = "redirect",
+ module = "medium",
+ view = "index"
+ }
+ },
+ content = function()
+ if id then
+ ui.field.integer{ label = _"Id", name = "id", readonly = true }
+ end
+ ui.field.select{
+ label = _"Media type",
+ name = "media_type_id",
+ foreign_records = MediaType:new_selector():exec(),
+ foreign_id = "id",
+ foreign_name = "name"
+ }
+ ui.field.text{ label = _"Name", name = "name" }
+ ui.field.boolean{ label = _"Copy protected", name = "copyprotected" }
+
+ ui.multiselect{
+ name = "genres[]",
+ label = _"Genres",
+ style = "select",
+ attr = { size = 5 },
+ foreign_records = Genre:new_selector():exec(),
+ connecting_records = medium and medium.classifications or {},
+ own_id = "id",
+ own_reference = "medium_id",
+ foreign_reference = "genre_id",
+ foreign_id = "id",
+ foreign_name = "name",
+ }
+ local tracks = medium and medium.tracks or {}
+ for i = 1, 5 do
+ tracks[#tracks+1] = Track:new()
+ end
+ ui.list{
+ label = _"Tracks",
+ prefix = "tracks",
+ records = tracks,
+ columns = {
+ {
+ label = _"Pos",
+ name = "position",
+ },
+ {
+ label = _"Name",
+ name = "name",
+ },
+ {
+ label = _"Description",
+ name = "description",
+ },
+ {
+ label = _"Duration",
+ name = "duration",
+ },
+ {
+ content = function()
+ ui.field.hidden{ name = "id" }
+ end
+ }
+ }
+ }
+
+ ui.submit{ text = _"Save" }
+ end
+ }
+end)
diff -r 000000000000 -r 9fdfb27f8e67 demo-app/app/main/user/_action/update.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/demo-app/app/main/user/_action/update.lua Sun Oct 25 12:00:00 2009 +0100
@@ -0,0 +1,24 @@
+local user
+local id = param.get_id()
+if id then
+ user = User:by_id(id)
+else
+ user = User:new()
+end
+
+if param.get("delete", atom.boolean) then
+ local name = user.name
+ user:destroy()
+ slot.put_into("notice", _("User '#{name}' deleted", {name = name}))
+ return
+end
+
+param.update(user, "ident", "password", "name", "write_priv", "admin")
+
+user:save()
+
+if id then
+ slot.put_into("notice", _("User '#{name}' updated", {name = user.name}))
+else
+ slot.put_into("notice", _("User '#{name}' created", {name = user.name}))
+end
diff -r 000000000000 -r 9fdfb27f8e67 demo-app/app/main/user/_filter/25_require_admin.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/demo-app/app/main/user/_filter/25_require_admin.lua Sun Oct 25 12:00:00 2009 +0100
@@ -0,0 +1,3 @@
+app.session.user:require_privilege("admin")
+
+execute.inner()
diff -r 000000000000 -r 9fdfb27f8e67 demo-app/app/main/user/index.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/demo-app/app/main/user/index.lua Sun Oct 25 12:00:00 2009 +0100
@@ -0,0 +1,56 @@
+slot.put_into("title", encode.html(_"Users"))
+
+slot.select("actions", function()
+ ui.link{
+ content = _"Create new user",
+ module = "user",
+ view = "show"
+ }
+end)
+
+
+local selector = User:new_selector():add_order_by('"ident", "id"')
+
+slot.select("main", function()
+ ui.paginate{
+ selector = selector,
+ content = function()
+ ui.list{
+ records = selector:exec(),
+ columns = {
+ {
+ field_attr = { style = "float: right;" },
+ label = _"Id",
+ name = "id"
+ },
+ {
+ label = _"Ident",
+ name = "ident"
+ },
+ {
+ label = _"Name",
+ name = "name"
+ },
+ {
+ label = _"w",
+ name = "write_priv"
+ },
+ {
+ label = _"Admin",
+ name = "admin"
+ },
+ {
+ content = function(record)
+ ui.link{
+ content = _"Show",
+ module = "user",
+ view = "show",
+ id = record.id
+ }
+ end
+ },
+ }
+ }
+ end
+ }
+end)
\ No newline at end of file
diff -r 000000000000 -r 9fdfb27f8e67 demo-app/app/main/user/show.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/demo-app/app/main/user/show.lua Sun Oct 25 12:00:00 2009 +0100
@@ -0,0 +1,65 @@
+local user
+local id = param.get_id()
+if id then
+ user = User:by_id(id)
+end
+
+if user then
+ slot.put_into("title", encode.html(_"User"))
+else
+ slot.put_into("title", encode.html(_"New user"))
+end
+
+slot.select("actions", function()
+ ui.link{
+ content = _"Back",
+ module = "user"
+ }
+ if user then
+ ui.link{
+ content = _"Delete",
+ form_attr = {
+ onsubmit = "return confirm('" .. _'Are you sure?' .. "');"
+ },
+ module = "user",
+ action = "update",
+ id = user.id,
+ params = { delete = true },
+ routing = {
+ default = {
+ mode = "redirect",
+ module = "user",
+ view = "index"
+ }
+ }
+ }
+ end
+end)
+
+slot.select("main", function()
+ ui.form{
+ attr = { class = "vertical" },
+ record = user,
+ module = "user",
+ action = "update",
+ id = id,
+ routing = {
+ default = {
+ mode = "redirect",
+ module = "user",
+ view = "index"
+ }
+ },
+ content = function()
+ if id then
+ ui.field.integer{ label = _"Id", name = "id", readonly = true }
+ end
+ ui.field.text{ label = _"Ident", name = "ident" }
+ ui.field.text{ label = _"Password", name = "password" }
+ ui.field.text{ label = _"Name", name = "name" }
+ ui.field.boolean{ label = _"Write Priv", name = "write_priv" }
+ ui.field.boolean{ label = _"Admin", name = "admin" }
+ ui.submit{ text = _"Save" }
+ end
+ }
+end)
diff -r 000000000000 -r 9fdfb27f8e67 demo-app/config/demo.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/demo-app/config/demo.lua Sun Oct 25 12:00:00 2009 +0100
@@ -0,0 +1,47 @@
+-- uncomment the following two lines to use C implementations of chosen
+-- functions and to disable garbage collection during the request, to
+-- increase speed:
+--
+-- require 'webmcp_accelerator'
+-- collectgarbage("stop")
+
+-- open and set default database handle
+db = assert(mondelefant.connect{
+ engine='postgresql',
+ dbname='webmcp_demo'
+})
+at_exit(function()
+ db:close()
+end)
+function mondelefant.class_prototype:get_db_conn() return db end
+
+-- enable output of SQL commands in trace system
+function db:sql_tracer(command)
+ return function(error_info)
+ local error_info = error_info or {}
+ trace.sql{ command = command, error_position = error_info.position }
+ end
+end
+
+-- 'request.get_relative_baseurl()' should be replaced by the absolute
+-- base URL of the application, as otherwise HTTP redirects will not be
+-- standard compliant
+request.set_absolute_baseurl(request.get_relative_baseurl())
+
+-- uncomment the following lines, if you want to use a database driven
+-- tempstore (for flash messages):
+--
+-- function tempstore.save(blob)
+-- return Tempstore:create(blob)
+-- end
+-- function tempstore.pop(key)
+-- return Tempstore:data_by_key(key)
+-- end
+
+
+function mondelefant.class_prototype:by_id(id)
+ return self:new_selector()
+ :add_where{ "id = ?", id }
+ :optional_object_mode()
+ :exec()
+end
diff -r 000000000000 -r 9fdfb27f8e67 demo-app/db/schema.sql
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/demo-app/db/schema.sql Sun Oct 25 12:00:00 2009 +0100
@@ -0,0 +1,79 @@
+-- only needed for database driven tempstore (see application config)
+CREATE TABLE "tempstore" (
+ "key" TEXT PRIMARY KEY,
+ "data" BYTEA NOT NULL );
+
+-- Attention: USER is a reserved word in PostgreSQL. We use it anyway in
+-- this example. Don't forget the double quotes where neccessary.
+CREATE TABLE "user" (
+ "id" SERIAL8 PRIMARY KEY,
+ "ident" TEXT NOT NULL,
+ "password" TEXT,
+ "name" TEXT,
+ "lang" TEXT,
+ "write_priv" BOOLEAN NOT NULL DEFAULT FALSE,
+ "admin" BOOLEAN NOT NULL DEFAULT FALSE );
+
+CREATE TABLE "session" (
+ "ident" TEXT PRIMARY KEY,
+ "csrf_secret" TEXT NOT NULL,
+ "expiry" TIMESTAMPTZ NOT NULL DEFAULT NOW() + '24 hours',
+ "user_id" INT8 REFERENCES "user" ("id") ON DELETE SET NULL ON UPDATE CASCADE );
+
+CREATE TABLE "media_type" (
+ "id" SERIAL8 PRIMARY KEY,
+ "name" TEXT NOT NULL,
+ "description" TEXT );
+
+CREATE TABLE "genre" (
+ "id" SERIAL8 PRIMARY KEY,
+ "name" TEXT NOT NULL,
+ "description" TEXT );
+
+CREATE TABLE "medium" (
+ "id" SERIAL8 PRIMARY KEY,
+ "media_type_id" INT8 NOT NULL REFERENCES "media_type" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
+ "name" TEXT NOT NULL,
+ "copyprotected" BOOLEAN NOT NULL );
+
+CREATE TABLE "classification" (
+ PRIMARY KEY ("medium_id", "genre_id"),
+ "medium_id" INT8 REFERENCES "medium" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
+ "genre_id" INT8 REFERENCES "genre" ("id") ON DELETE CASCADE ON UPDATE CASCADE );
+
+CREATE TABLE "track" (
+ "id" SERIAL8 PRIMARY KEY,
+ "medium_id" INT8 NOT NULL REFERENCES "medium" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
+ "position" INT8 NOT NULL,
+ "name" TEXT NOT NULL,
+ "description" TEXT,
+ "duration" INTERVAL,
+ UNIQUE ("medium_id", "position") );
+
+INSERT INTO "user" ("ident", "password", "name", "write_priv", "admin")
+ VALUES ('admin', 'admin', 'Administrator', true, true);
+
+INSERT INTO "user" ("ident", "password", "name", "write_priv", "admin")
+ VALUES ('user', 'User', 'User', true, false);
+
+INSERT INTO "user" ("ident", "password", "name", "write_priv", "admin")
+ VALUES ('anon', 'anon', 'Anonymous', false, false);
+
+INSERT INTO "media_type" ("name", "description") VALUES ('CD', '');
+INSERT INTO "media_type" ("name", "description") VALUES ('Tape', '');
+
+INSERT INTO "genre" ("name", "description") VALUES ('Klassik', '');
+INSERT INTO "genre" ("name", "description") VALUES ('Gospel', '');
+INSERT INTO "genre" ("name", "description") VALUES ('Jazz', '');
+INSERT INTO "genre" ("name", "description") VALUES ('Traditional', '');
+INSERT INTO "genre" ("name", "description") VALUES ('Latin', '');
+INSERT INTO "genre" ("name", "description") VALUES ('Blues', '');
+INSERT INTO "genre" ("name", "description") VALUES ('Rhythm & blues', '');
+INSERT INTO "genre" ("name", "description") VALUES ('Funk', '');
+INSERT INTO "genre" ("name", "description") VALUES ('Rock', '');
+INSERT INTO "genre" ("name", "description") VALUES ('Pop', '');
+INSERT INTO "genre" ("name", "description") VALUES ('Country', '');
+INSERT INTO "genre" ("name", "description") VALUES ('Electronic', '');
+INSERT INTO "genre" ("name", "description") VALUES ('Ska / Reggea', '');
+INSERT INTO "genre" ("name", "description") VALUES ('Hip hop / Rap', '');
+
diff -r 000000000000 -r 9fdfb27f8e67 demo-app/locale/translations.de.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/demo-app/locale/translations.de.lua Sun Oct 25 12:00:00 2009 +0100
@@ -0,0 +1,59 @@
+#!/usr/bin/env lua
+return {
+["Admin"] = "Admin";
+["Are you sure?"] = "Bist Du sicher?";
+["Back"] = "Zurück";
+["Copy protected"] = "Kopiergeschützt";
+["Create new genre"] = "Neues Genre anlegen";
+["Create new media type"] = "Neuen Medientyp anlegen";
+["Create new medium"] = "Neues Medium anlegen";
+["Create new user"] = "Neuen Benutzer anlegen";
+["Delete"] = "Löschen";
+["Description"] = "Beschreibung";
+["Duration"] = "Dauer";
+["Genre"] = "Genre";
+["Genre '#{name}' created"] = "Genre '#{name}' angelegt";
+["Genre '#{name}' deleted"] = "Genre '#{name}' gelöscht";
+["Genre '#{name}' updated"] = "Genre '#{name}' aktualisiert";
+["Genres"] = "Genres";
+["Home"] = "Startseite";
+["Id"] = "Id";
+["Ident"] = "Ident";
+["Invalid username or password!"] = "Ungülter Benutzername oder Kennwort";
+["Language changed"] = "Sprache gewechselt";
+["Login"] = "Anmeldung";
+["Login successful!"] = "Anmeldung erfolgreich!";
+["Logout"] = "Abmelden";
+["Logout successful"] = "Anmeldung erfolgreich";
+["Media"] = "Medium";
+["Media type"] = "Medientyp";
+["Media type '#{name}' created"] = "Medientyp '#{name}' angelegt";
+["Media type '#{name}' deleted"] = "Medientyp '#{name}' gelöscht";
+["Media type '#{name}' updated"] = "Medientyp '#{name}' aktualisiert";
+["Media types"] = "Medientypen";
+["Medium"] = "Medium";
+["Medium '#{name}' created"] = "Medium '#{name}' angelegt";
+["Medium '#{name}' deleted"] = "Medium '#{name}' gelöscht";
+["Medium '#{name}' updated"] = "Medium '#{name}' aktualisiert";
+["Name"] = "Name";
+["New genre"] = "Neues Genre";
+["New media type"] = "Neuer Medientyp";
+["New medium"] = "Neues Medium";
+["New user"] = "Neuer Benutzer";
+["Password"] = "Kennwort";
+["Password login"] = "Anmeldung mit Kennwort";
+["Pos"] = "Pos";
+["Save"] = "Speichern";
+["Show"] = "Anzeigen";
+["Tracks"] = "Stücke";
+["User"] = "Benutzer";
+["User '#{name}' created"] = "Benutzer '#{name}' angelegt";
+["User '#{name}' deleted"] = "Benutzer '#{name}' gelöscht";
+["User '#{name}' updated"] = "Benutzer '#{name}' aktualisiert";
+["Username"] = "Benutzername";
+["Users"] = "Benutzer";
+["Welcome to webmcp demo application"] = "Willkommen zur webmcp Demo-Anwendung";
+["Write Priv"] = "Schreibrecht";
+["w"] = "w";
+["webmcp demo application"] = "webmcp Demo-Anwendung";
+}
diff -r 000000000000 -r 9fdfb27f8e67 demo-app/locale/translations.en.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/demo-app/locale/translations.en.lua Sun Oct 25 12:00:00 2009 +0100
@@ -0,0 +1,59 @@
+#!/usr/bin/env lua
+return {
+["Admin"] = false;
+["Are you sure?"] = false;
+["Back"] = false;
+["Copy protected"] = false;
+["Create new genre"] = false;
+["Create new media type"] = false;
+["Create new medium"] = false;
+["Create new user"] = false;
+["Delete"] = false;
+["Description"] = false;
+["Duration"] = false;
+["Genre"] = false;
+["Genre '#{name}' created"] = false;
+["Genre '#{name}' deleted"] = false;
+["Genre '#{name}' updated"] = false;
+["Genres"] = false;
+["Home"] = false;
+["Id"] = false;
+["Ident"] = false;
+["Invalid username or password!"] = false;
+["Language changed"] = false;
+["Login"] = false;
+["Login successful!"] = false;
+["Logout"] = false;
+["Logout successful"] = false;
+["Media"] = false;
+["Media type"] = false;
+["Media type '#{name}' created"] = false;
+["Media type '#{name}' deleted"] = false;
+["Media type '#{name}' updated"] = false;
+["Media types"] = false;
+["Medium"] = false;
+["Medium '#{name}' created"] = false;
+["Medium '#{name}' deleted"] = false;
+["Medium '#{name}' updated"] = false;
+["Name"] = false;
+["New genre"] = false;
+["New media type"] = false;
+["New medium"] = false;
+["New user"] = false;
+["Password"] = false;
+["Password login"] = false;
+["Pos"] = false;
+["Save"] = false;
+["Show"] = false;
+["Tracks"] = false;
+["User"] = false;
+["User '#{name}' created"] = false;
+["User '#{name}' deleted"] = false;
+["User '#{name}' updated"] = false;
+["Username"] = false;
+["Users"] = false;
+["Welcome to webmcp demo application"] = false;
+["Write Priv"] = false;
+["w"] = false;
+["webmcp demo application"] = false;
+}
diff -r 000000000000 -r 9fdfb27f8e67 demo-app/locale/translations.es.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/demo-app/locale/translations.es.lua Sun Oct 25 12:00:00 2009 +0100
@@ -0,0 +1,59 @@
+#!/usr/bin/env lua
+return {
+["Admin"] = "Admin";
+["Are you sure?"] = "Estás seguro?";
+["Back"] = "Atrás";
+["Copy protected"] = "Protegido anticopia";
+["Create new genre"] = "Crear un nuevo género";
+["Create new media type"] = "Crear un nuevo soporte";
+["Create new medium"] = "Crear un nuevo medio";
+["Create new user"] = "Crear un nuevo usuario";
+["Delete"] = "Borrar";
+["Description"] = "Descripción";
+["Duration"] = "Duración";
+["Genre"] = "Género";
+["Genre '#{name}' created"] = "Género '#{name}' creado";
+["Genre '#{name}' deleted"] = "Género '#{name}' borrado";
+["Genre '#{name}' updated"] = "Género '#{name}' actualizado";
+["Genres"] = "Géneros";
+["Home"] = "Inicio";
+["Id"] = "Id";
+["Ident"] = "Ident";
+["Invalid username or password!"] = "Nombre de usuario o Contraseña Incorrecta";
+["Language changed"] = "Idioma cambiado";
+["Login"] = "Login";
+["Login successful!"] = "Login realizado!";
+["Logout"] = "Logout";
+["Logout successful"] = "Logout realizado";
+["Media"] = "Medios";
+["Media type"] = "Soporte";
+["Media type '#{name}' created"] = "Soporte '#{name}' creado";
+["Media type '#{name}' deleted"] = "Soporte '#{name}' borrado";
+["Media type '#{name}' updated"] = "Soporte '#{name}' actualizado";
+["Media types"] = "Soporte";
+["Medium"] = "Medio";
+["Medium '#{name}' created"] = "Medio '#{name}' creado";
+["Medium '#{name}' deleted"] = "Medio '#{name}' borrado";
+["Medium '#{name}' updated"] = "Medio '#{name}' actualizado";
+["Name"] = "Nombre";
+["New genre"] = "Nuevo género";
+["New media type"] = "Nuevo soporte";
+["New medium"] = "Nuevo medio";
+["New user"] = "Nuevo usuario";
+["Password"] = "Contraseña";
+["Password login"] = "Login con contraseña";
+["Pos"] = "Pos";
+["Save"] = "Guardar";
+["Show"] = "Mostrar";
+["Tracks"] = "Pistas";
+["User"] = "Usuario";
+["User '#{name}' created"] = "Usuario '#{name}' creado";
+["User '#{name}' deleted"] = "Usuario '#{name}' borrado";
+["User '#{name}' updated"] = "Usuario '#{name}' actualizado";
+["Username"] = "Nombre de usuario";
+["Users"] = "Usuarios";
+["Welcome to webmcp demo application"] = "Bienvenido a la aplicación de demostración de webmcp";
+["Write Priv"] = "Permiso de escritura";
+["w"] = "w";
+["webmcp demo application"] = "Aplicación de demostración de webmcp";
+}
diff -r 000000000000 -r 9fdfb27f8e67 demo-app/model/classification.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/demo-app/model/classification.lua Sun Oct 25 12:00:00 2009 +0100
@@ -0,0 +1,22 @@
+Classification = mondelefant.new_class()
+Classification.table = 'classification'
+
+Classification:add_reference{
+ mode = 'm1', -- many (m) Classifications can refer to one (1) Medium
+ to = "Medium", -- name of referenced model (quoting avoids auto-loading of model here)
+ this_key = 'medium_id', -- our key in the classification table
+ that_key = 'id', -- other key in the medium table
+ ref = 'medium', -- name of reference
+ back_ref = nil, -- not used for m1 relation!
+ default_order = nil -- not used for m1 relation!
+}
+
+Classification:add_reference{
+ mode = 'm1', -- many (m) Classifications can refer to one (1) Medium
+ to = "Genre", -- name of referenced model (quoting avoids auto-loading of model here)
+ this_key = 'genre_id', -- our key in the classification table
+ that_key = 'id', -- other key in the genre table
+ ref = 'genre', -- name of reference
+ back_ref = nil, -- not used for m1 relation!
+ default_order = nil -- not used for m1 relation!
+}
diff -r 000000000000 -r 9fdfb27f8e67 demo-app/model/genre.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/demo-app/model/genre.lua Sun Oct 25 12:00:00 2009 +0100
@@ -0,0 +1,25 @@
+Genre = mondelefant.new_class()
+Genre.table = 'genre'
+
+Genre:add_reference{
+ mode = '1m', -- one (1) Genre is used for many (m) Classifications
+ to = "Classification", -- name of referenced model (using a string instead of reference avoids auto-loading here)
+ this_key = 'id', -- own key in genre table
+ that_key = 'genre_id', -- other key in classification table
+ ref = 'classifications', -- name of reference
+ back_ref = 'genre', -- each autoloaded Classification automatically refers back to the Genre
+ default_order = '"media_id"' -- order Classifications by SQL expression "media_id"
+}
+
+Genre:add_reference{
+ mode = 'mm', -- many (m) Genres belong to many (m) Medium entries
+ to = "Medium", -- name of referenced model (quoting avoids auto-loading here)
+ this_key = 'id', -- (primary) key of genre table
+ that_key = 'id', -- (primary) key of medium talbe
+ connected_by_table = 'classification', -- table connecting genres with media
+ connected_by_this_key = 'genre_id', -- key in connection table referencing genres
+ connected_by_that_key = 'medium_id', -- key in connection table referencing media
+ ref = 'media', -- name of reference
+ back_ref = nil, -- not used for mm relation!
+ default_order = '"medium"."name", "medium"."id"' -- mm references need qualified names in SQL order expression!
+}
diff -r 000000000000 -r 9fdfb27f8e67 demo-app/model/media_type.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/demo-app/model/media_type.lua Sun Oct 25 12:00:00 2009 +0100
@@ -0,0 +1,12 @@
+MediaType = mondelefant.new_class()
+MediaType.table = 'media_type'
+
+MediaType:add_reference{
+ mode = '1m', -- one (1) MediaType is set for many (m) media
+ to = "Medium", -- name of referenced model (quoting avoids auto-loading here)
+ this_key = 'id', -- own key in media_type table
+ that_key = 'media_type_id', -- other key in medium table
+ ref = 'media', -- name of reference
+ back_ref = 'media_type', -- each autoloaded Medium automatically refers back to the MediaType
+ default_order = '"name", "id"' -- order media by SQL expression "name", "id"
+}
diff -r 000000000000 -r 9fdfb27f8e67 demo-app/model/medium.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/demo-app/model/medium.lua Sun Oct 25 12:00:00 2009 +0100
@@ -0,0 +1,45 @@
+Medium = mondelefant.new_class()
+Medium.table = 'medium'
+
+Medium:add_reference{
+ mode = 'm1', -- many (m) Medium entries can refer to one (1) MediaType
+ to = "MediaType", -- name of referenced model (quoting avoids auto-loading here)
+ this_key = 'media_type_id', -- own key in medium table
+ that_key = 'id', -- other key in media_type table
+ ref = 'media_type', -- name of reference
+ back_ref = nil, -- not used for m1 relation!
+ default_order = nil -- not used for m1 relation!
+}
+
+Medium:add_reference{
+ mode = '1m', -- one (1) Medium has many (m) Classifications
+ to = "Classification", -- name of referenced model (quoting avoids auto-loading here)
+ this_key = 'id', -- own key in medium table
+ that_key = 'medium_id', -- other key in classification table
+ ref = 'classifications', -- name of reference
+ back_ref = 'medium', -- each autoloaded classification automatically refers back to the Medium
+ default_order = '"genre_id"' -- order classifications by SQL expression "genre_id"
+}
+
+Medium:add_reference{
+ mode = 'mm', -- many (m) Media belong to many (m) Genres
+ to = "Genre", -- name of referenced model (quoting avoids auto-loading here)
+ this_key = 'id', -- (primary) key of medium table
+ that_key = 'id', -- (primary) key of genre talbe
+ connected_by_table = 'classification', -- table connecting media with genres
+ connected_by_this_key = 'medium_id', -- key in classification table referencing media
+ connected_by_that_key = 'genre_id', -- key in classification table referencing genres
+ ref = 'genres', -- name of reference
+ back_ref = nil, -- not used for mm relation!
+ default_order = '"genre"."name", "genre"."id"' -- mm references need qualified names in SQL order expression!
+}
+
+Medium:add_reference{
+ mode = '1m', -- one (1) Medium has many (m) Tracks
+ to = "Track", -- name of referenced model (quoting avoids auto-loading here)
+ this_key = 'id', -- own key in medium table
+ that_key = 'medium_id', -- other key in track table
+ ref = 'tracks', -- name of reference
+ back_ref = 'medium', -- each autoloaded classification automatically refers back to the Medium
+ default_order = '"position"' -- order tracks by SQL expression "position"
+}
diff -r 000000000000 -r 9fdfb27f8e67 demo-app/model/session.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/demo-app/model/session.lua Sun Oct 25 12:00:00 2009 +0100
@@ -0,0 +1,35 @@
+Session = mondelefant.new_class()
+Session.table = 'session'
+Session.primary_key = { "ident" }
+
+Session:add_reference{
+ mode = 'm1', -- many (m) sessions refer to one (1) user
+ to = "User", -- name of referenced model (quoting avoids auto-loading here)
+ this_key = 'user_id', -- own key in session table
+ that_key = 'id', -- other key in user table
+ ref = 'user', -- name of reference
+ back_ref = nil, -- not used for m1 relation!
+ default_order = nil -- not used for m1 relation!
+}
+
+local function random_string()
+ return multirand.string(
+ 32,
+ '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
+ )
+end
+
+function Session:new()
+ local session = self.prototype.new(self) -- super call
+ session.ident = random_string()
+ session.csrf_secret = random_string()
+ session:save()
+ return session
+end
+
+function Session:by_ident(ident)
+ local selector = self:new_selector()
+ selector:add_where{ 'ident = ?', ident }
+ selector:optional_object_mode()
+ return selector:exec()
+end
diff -r 000000000000 -r 9fdfb27f8e67 demo-app/model/tempstore.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/demo-app/model/tempstore.lua Sun Oct 25 12:00:00 2009 +0100
@@ -0,0 +1,25 @@
+Tempstore = mondelefant.new_class()
+Tempstore.table = 'tempstore'
+
+function Tempstore:by_key(key)
+ local selector = self:new_selector()
+ selector:add_where{ 'key = ?', key }
+ selector:optional_object_mode()
+ return selector:exec()
+end
+
+function Tempstore:data_by_key(key)
+ local tempstore = Tempstore:by_key(key)
+ if tempstore then
+ tempstore:destroy()
+ return tempstore.data
+ end
+end
+
+function Tempstore:create(data)
+ tempstore = Tempstore:new()
+ tempstore.key = multirand.string(22, '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz')
+ tempstore.data = data
+ tempstore:save()
+ return tempstore.key
+end
diff -r 000000000000 -r 9fdfb27f8e67 demo-app/model/track.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/demo-app/model/track.lua Sun Oct 25 12:00:00 2009 +0100
@@ -0,0 +1,12 @@
+Track = mondelefant.new_class()
+Track.table = 'track'
+
+Track:add_reference{
+ mode = 'm1', -- many (m) Tracks can refer to one (1) Medium
+ to = "Medium", -- name of referenced model (quoting avoids auto-loading of model here)
+ this_key = 'medium_id', -- our key in the track table
+ that_key = 'id', -- other key in the medium table
+ ref = 'medium', -- name of reference
+ back_ref = nil, -- not used for m1 relation!
+ default_order = nil -- not used for m1 relation!
+}
diff -r 000000000000 -r 9fdfb27f8e67 demo-app/model/user.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/demo-app/model/user.lua Sun Oct 25 12:00:00 2009 +0100
@@ -0,0 +1,33 @@
+User = mondelefant.new_class()
+User.table = 'user'
+
+User:add_reference{
+ mode = '1m', -- one (1) user can have many (m) sessions
+ to = "Session", -- referenced model (quoting avoids auto-loading here)
+ this_key = 'id', -- own key in user table
+ that_key = 'user_id', -- other key in session table
+ ref = 'sessions', -- name of reference
+ back_ref = 'user', -- each autoloaded Session automatically refers back to the User
+ default_order = '"ident"' -- order sessions by SQL expression "ident"
+}
+
+function User:by_ident_and_password(ident, password)
+ local selector = self:new_selector()
+ selector:add_where{ 'ident = ? AND password = ?', ident, password }
+ selector:optional_object_mode()
+ return selector:exec()
+end
+
+function User.object_get:name_with_login()
+ return self.name .. ' (' .. self.login .. ')'
+end
+
+function User.object:require_privilege(privilege)
+ if privilege == "admin" then
+ assert(self.admin, "Administrator privilege required")
+ elseif privilege == "write" then
+ assert(self.write_priv, "Write privilege required")
+ else
+ error("Unknown privilege passed to require_privilege method of User")
+ end
+end
diff -r 000000000000 -r 9fdfb27f8e67 demo-app/static/lang/de.png
Binary file demo-app/static/lang/de.png has changed
diff -r 000000000000 -r 9fdfb27f8e67 demo-app/static/lang/en.png
Binary file demo-app/static/lang/en.png has changed
diff -r 000000000000 -r 9fdfb27f8e67 demo-app/static/lang/es.png
Binary file demo-app/static/lang/es.png has changed
diff -r 000000000000 -r 9fdfb27f8e67 demo-app/static/logo.png
Binary file demo-app/static/logo.png has changed
diff -r 000000000000 -r 9fdfb27f8e67 demo-app/static/style.css
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/demo-app/static/style.css Sun Oct 25 12:00:00 2009 +0100
@@ -0,0 +1,383 @@
+/*
+ * ********** body ***********
+ */
+
+body {
+ background-color: #ddd;
+ font-family: sans-serif;
+ margin: 0;
+ padding: 0;
+ font-size: 10pt;
+}
+
+body, td, th, input, select {
+ font-size: 10pt;
+}
+
+div {
+ padding: 0px;
+ margin: 0px;
+}
+
+/*
+ * ********** Logo ***********
+ */
+
+.layout_logo {
+ background: #fff;
+ border: 1px solid #444;
+ border-left: none;
+ border-top: none;
+ color: #444;
+ float: left;
+ height: 2.29em;
+ margin-bottom: 2ex;
+ padding-bottom: 0.3ex;
+ padding-left: 0.3ex;
+ padding-top: 0.3ex;
+}
+
+.logo {
+ font-size: 1em;
+ font-weight: bold;
+}
+
+.logo img {
+ height: 2.29em;
+ margin-bottom: -0.7em;
+}
+
+/*
+ * ********** Top navigation ***********
+ */
+
+.layout_topnav {
+ float: left;
+ margin-bottom: 2em;
+ margin-left: 2ex;
+}
+
+.slot_topnav .nav {
+ background: #fff;
+ border: 1px solid #444;
+ border-top: none;
+ color: #000;
+ display: block;
+ float: left;
+ font-weight: bold;
+ height: 2.29em;
+ margin-right: 1ex;
+ padding-bottom: 0.3ex;
+ padding-left: 0.3ex;
+ padding-right: 1ex;
+ padding-top: 0.3ex;
+ text-decoration: none;
+}
+
+
+
+.vertical .lang_chooser {
+ margin-left: 25ex;
+ margin-bottom: 1ex;
+}
+
+.vertical .lang_chooser div {
+ margin-right: 1ex;
+}
+
+.lang_chooser div {
+ float: left;
+ text-align: center;
+}
+
+.lang_chooser a {
+ color: #000;
+ font-size: 75%;
+}
+
+.lang_chooser img {
+ display: block;
+}
+
+.lang_chooser img {
+ border: 1px solid white;
+}
+
+.lang_chooser a:hover img {
+ border: 1px solid #444;
+}
+
+.lang_chooser a:hover {
+ background: #444;
+ color: #fff;
+}
+
+
+.slot_topnav a:hover {
+ background: #444;
+ color: #fff;
+}
+
+/*
+ * ********** Content ***********
+ */
+
+.layout_content {
+ margin-left: 2ex;
+ margin-right: 2ex;
+}
+
+
+/*
+ * ********** Messages ***********
+ */
+
+.layout_notice, .layout_error, .layout_warning {
+ background: #fff;
+ font-weight: bold;
+ right: 2ex;
+ line-height: 1.7em;
+ position: absolute;
+ top: 2ex;
+ width: 60ex;
+ -moz-opacity:0.7;
+}
+
+.slot_notice, .slot_warning, .slot_error {
+ padding: 2ex;
+}
+
+.slot_notice {
+ color: green;
+ border: 2px solid green;
+}
+
+.slot_warning {
+ color: orange;
+ border: 2px solid orange;
+}
+
+.slot_error {
+ color: red;
+ border: 2px solid red;
+}
+
+
+
+/*
+ * ********** Title, Actions and Main ***********
+ */
+
+.layout_title {
+ background: #fff;
+ border: 1px solid #444;
+ border-bottom: none;
+ padding: 0.5ex;
+ font-size: 120%;
+ font-weight: bold;
+ float: left;
+}
+
+.layout_title .object_class {
+ float: left;
+ font-size: 0.7em;
+ font-weight: normal;
+ margin-right: 1ex;
+}
+
+.layout_actions {
+ background: #444;
+ border-left: 1px solid #444;
+ padding-top: 0.5ex;
+ padding-bottom: 0.5ex;
+}
+
+.slot_actions a {
+ color: #fff;
+ text-decoration: none;
+ padding: 0.5ex;
+}
+
+.slot_actions a:hover {
+ background: #ccc;
+ color: #444;
+}
+
+.slot_main {
+ background: #fff;
+ border: 1px solid #444;
+ border-top: none;
+ padding: 2ex;
+ padding-bottom: 0px;
+}
+
+
+
+/*
+ * ********** ui ***********
+ */
+
+
+form.ui_link {
+ display: inline;
+ margin: 0px;
+ padding: 0px;
+}
+
+.vertical {
+ margin-bottom: 1em;
+}
+
+.vertical:after, .vertical div:after {
+ clear: left;
+ content: ".";
+ display: block;
+ height: 0;
+ visibility: hidden;
+}
+
+.vertical label {
+ float: left;
+ width: 25ex;
+ padding-top: 0.7em;
+ font-size: 80%;
+ font-weight: bold;
+ color: #666;
+ text-align: right;
+ text-transform: uppercase;
+}
+
+.vertical select,
+.vertical input,
+.vertical textarea,
+.vertical span {
+ margin-left: 1em;
+ margin-bottom: 2ex;
+}
+
+.vertical textarea {
+ border: none;
+ border: 1px dotted #777;
+ padding: 0px;
+}
+
+.vertical input,
+.vertical select {
+ border: 1px dotted #777;
+}
+
+.vertical textarea {
+ width: 80ex;
+ height: 5em;
+ font-family: sans-serif;
+ font-size: 100%;
+ padding: 0.5ex;
+}
+
+input[type=text] {
+ background: #fff;
+ padding: 0.5ex;
+}
+
+input[type=submit] {
+ margin-left: 25ex;
+ border: 1px dotted #777;
+}
+
+input[type=submit]:hover {
+ border: 1px solid #777;
+}
+
+/* ui.list */
+
+table.ui_list {
+ border-collapse: collapse;
+}
+
+ul.ui_list {
+ padding: 0px;
+ margin: 0px;
+}
+
+.ui_list li {
+ clear: left;
+ list-style-type: none;
+}
+
+.ui_list_title {
+ font-size: 110%;
+ font-weight: bold;
+ margin-bottom: 0.2em;
+}
+
+
+li.ui_list_head {
+ font-weight: bold;
+ background: #777;
+ color: #fff;
+}
+
+.ui_list li:hover,
+.ui_list tr:hover {
+ background: #ddd;
+}
+
+.ui_list li.ui_list_head:hover,
+.ui_list tr.ui_list_head:hover {
+ background: #777;
+}
+
+.ui_list a {
+ text-decoration: none;
+ color: #000;
+ border-bottom: 1px dashed #777;
+}
+
+.ui_list a:hover {
+ border-bottom: 1px solid #000;
+}
+
+.ui_list li div {
+ float: left;
+}
+
+.ui_list td {
+ vertical-align: top;
+}
+
+.ui_list li div,
+.ui_list td {
+ padding-top: 0.4ex;
+ padding-bottom: 0.4ex;
+ padding-right: 0.4em;
+}
+
+.ui_list li div div {
+ padding: 0px;
+}
+
+.ui_list li div a {
+ text-decoration: none;
+ color: #000;
+}
+
+.ui_list_col_align_right {
+ text-align: right;
+}
+
+.ui_paginate_select {
+ text-align: center;
+}
+
+.ui_paginate_select a {
+ padding-left: 1ex;
+ padding-right: 1ex;
+ background-color: #ccc;
+ color: #000;
+}
+
+.ui_paginate_select a.active,
+.ui_paginate_select a:hover {
+ background-color: #444;
+ color: #fff;
+}
diff -r 000000000000 -r 9fdfb27f8e67 demo-app/static/trace.css
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/demo-app/static/trace.css Sun Oct 25 12:00:00 2009 +0100
@@ -0,0 +1,153 @@
+.layout_trace {
+ position: absolute;
+ right: 0;
+ margin-top: 20px;
+ border: 1px solid #404040;
+ font-size: 70%;
+ padding: 5px;
+ background: #ffe0c0;
+}
+
+#trace_show {
+ cursor: pointer;
+}
+
+.trace_list {
+ margin: 0px;
+ margin-bottom: 10px;
+ padding: 0px;
+ list-style-type: none;
+}
+
+.trace_list .trace_list {
+ border-top: 1px solid;
+ margin-bottom: 0px;
+}
+
+.trace_list li {
+ margin: 3px;
+ padding: 0px;
+}
+
+.trace_head {
+ font-weight: bold;
+ margin: 1px;
+}
+
+.trace_error {
+ border: 3px solid red;
+ background-color: black;
+ color: #c00000;
+ text-align: center;
+ text-decoration: blink;
+}
+
+.trace_error_position {
+ color: red;
+ text-decoration: blink;
+ font-weight: bold;
+}
+
+.trace_config {
+ border: 1px solid #608000;
+ background-color: #ffffff;
+ color: #608000;
+}
+
+.trace_config .trace_list {
+ border-color: #608000;
+}
+
+.trace_request {
+ border: 1px solid #6000ff;
+ background-color: #c080ff;
+ color: #6000ff;
+}
+
+.trace_request .trace_list {
+ border-color: #6000ff;
+}
+
+.trace_filter {
+ border: 1px solid #606060;
+ background-color: #c0c0c0;
+ color: #606060;
+}
+
+.trace_filter .trace_list {
+ border-color: #606060;
+}
+
+.trace_view {
+ border: 1px solid #0000ff;
+ background-color: #40c0ff;
+ color: #0000ff;
+}
+
+.trace_view .trace_list {
+ border-color: #0000ff;
+}
+
+.trace_action_success {
+ border: 1px solid #006000;
+ background-color: #80ff80;
+ color: #006000;
+}
+
+.trace_action_success .trace_list {
+ border-color: #006000;
+}
+
+.trace_action_softfail {
+ border: 1px solid #600000;
+ background-color: #ff6020;
+ color: #600000;
+}
+
+.trace_action_softfail .trace_list {
+ border-color: #600000;
+}
+
+.trace_action_status {
+ font-weight: bold;
+}
+
+.trace_action_neutral {
+ border: 1px solid #600000;
+ background-color: #ffc040;
+ color: #600000;
+}
+
+.trace_action_neutral .trace_list {
+ border-color: #600000;
+}
+
+.trace_redirect, .trace_forward {
+ border: 1px solid #404000;
+ background-color: #c08040;
+ color: #404000;
+}
+
+.trace_redirect .trace_list, .trace_forward .trace_list {
+ border-color: #404000;
+}
+
+.trace_exectime {
+ border: 1px solid black;
+ background-color: #404040;
+ color: white;
+}
+
+.trace_exectime .trace_list {
+ border-color: white;
+}
+
+.trace_close {
+ border: 1px solid black;
+ background-color: #605040;
+ margin: 3px;
+ padding: 3px;
+ color: white;
+ text-align: center;
+ cursor: pointer;
+}
diff -r 000000000000 -r 9fdfb27f8e67 demo-app/tmp/.keep
diff -r 000000000000 -r 9fdfb27f8e67 doc/apache.sample.conf
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/apache.sample.conf Sun Oct 25 12:00:00 2009 +0100
@@ -0,0 +1,37 @@
+# Apache modules cgi_module, env, rewrite and alias must be loaded before
+# Take a look in your main apache configuration!
+
+RewriteEngine on
+# do not rewrite static URLs
+RewriteRule ^/webmcp-demo/static/(.*)$ /webmcp-demo/static/$1
+# base URL
+RewriteRule ^/webmcp-demo/(\?(.*))?$ /webmcp-demo/webmcp-wrapper.lua?_webmcp_urldepth=0&_webmcp_module=index&_webmcp_view=index&$2
+# module base URLs
+RewriteRule ^/webmcp-demo/([^/\?]+)/(\?(.*))?$ /webmcp-demo/webmcp-wrapper.lua?_webmcp_urldepth=1&_webmcp_module=$1&_webmcp_view=index&$3
+# actions
+RewriteRule ^/webmcp-demo/([^/\?]+)/([^/\.\?]+)(\?(.*))?$ /webmcp-demo/webmcp-wrapper.lua?_webmcp_urldepth=1&_webmcp_module=$1&_webmcp_action=$2&$4
+# views without numeric id or string ident
+RewriteRule ^/webmcp-demo/([^/\?]+)/([^/\.\?]+)\.([^/\?]+)(\?(.*))?$ "/webmcp-demo/webmcp-wrapper.lua?_webmcp_urldepth=1&_webmcp_module=$1&_webmcp_view=$2&_webmcp_suffix=$3&$5
+# views with numeric id or string ident
+RewriteRule ^/webmcp-demo/([^/\?]+)/([^/\?]+)/([^/\.\?]+)\.([^/\?]+)(\?(.*))?$ /webmcp-demo/webmcp-wrapper.lua?_webmcp_urldepth=2&_webmcp_module=$1&_webmcp_view=$2&_webmcp_id=$3&_webmcp_suffix=$4&$6
+
+# Directly serve static files
+Alias /webmcp-demo/static /__INSERT_LOCAL_FILE_PATH_TO_DEMO_APPLICATION_HERE__/static
+
+# Connect extarnal path to the webmcp cgi interface
+ScriptAlias /webmcp-demo/ /__INSERT_LOCAL_FILE_PATH_TO_WEBMCP_FRAMEWORK_HERE__/cgi-bin/
+
+# Allow CGI execution for the webmcp CGI interface
+
+ AllowOverride None
+ Options ExecCGI -MultiViews +SymLinksIfOwnerMatch
+ Order allow,deny
+ Allow from all
+
+
+# Configure environment for demo application
+
+ SetEnv WEBMCP_APP_BASEPATH '/__INSERT_LOCAL_FILE_PATH_TO_DEMO_APPLICATION_HERE__'
+ SetEnv WEBMCP_CONFIG_NAME 'demo'
+
+
diff -r 000000000000 -r 9fdfb27f8e67 doc/autodoc-footer.htmlpart
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/autodoc-footer.htmlpart Sun Oct 25 12:00:00 2009 +0100
@@ -0,0 +1,7 @@
+
+
+
+ Copyright (c) 2009 Public Software Group e. V., Berlin
+
+