# HG changeset patch # User jbe # Date 1258455600 -3600 # Node ID 985024b165202b59b2518cf6dc2271e1723a8218 # Parent 9fdfb27f8e676922467f3f14af94dd05427bf60c Version 1.0.1 New feature: JSON requests Changes in ui.paginate: Current page setting is directly fetched from CGI params, instead of view params Changed behavior of load methods of atom library to accept nil as input Bugfixes in mondelefant_atom_connector timestamp(tz) loaders Added global constant _WEBMCP_VERSION containing a version string diff -r 9fdfb27f8e67 -r 985024b16520 LICENSE --- a/LICENSE Sun Oct 25 12:00:00 2009 +0100 +++ b/LICENSE Tue Nov 17 12:00:00 2009 +0100 @@ -1,4 +1,4 @@ -Copyright (c) 2009 Public Software Group e. V., Berlin +Copyright (c) 2009 Public Software Group e. V., Berlin, Germany Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), diff -r 9fdfb27f8e67 -r 985024b16520 doc/autodoc-header.htmlpart --- a/doc/autodoc-header.htmlpart Sun Oct 25 12:00:00 2009 +0100 +++ b/doc/autodoc-header.htmlpart Tue Nov 17 12:00:00 2009 +0100 @@ -55,10 +55,10 @@ color: #505050; } - WebMCP 1.0.0 Documentation + WebMCP 1.0.1 Documentation -

WebMCP 1.0.0 Documentation

+

WebMCP 1.0.1 Documentation

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.

diff -r 9fdfb27f8e67 -r 985024b16520 framework/cgi-bin/webmcp.lua --- a/framework/cgi-bin/webmcp.lua Sun Oct 25 12:00:00 2009 +0100 +++ b/framework/cgi-bin/webmcp.lua Tue Nov 17 12:00:00 2009 +0100 @@ -1,5 +1,7 @@ #!/usr/bin/env lua +_WEBMCP_VERSION = "1.0.1" + -- include "../lib/" in search path for libraries do package.path = '../lib/?.lua;' .. package.path @@ -430,16 +432,14 @@ cgi.set_status(http_status) end - -- ajax - if request.is_ajax() then - cgi.set_content_type('text/html') - for i, slot_ident in ipairs{'main', 'actions', 'title', 'topnav', 'sidenav', 'debug', 'notice', 'warning', 'error'} do - local html = slot.get_content(slot_ident) - if html then - cgi.send_data("document.getElementById('" .. slot_ident .. "').innerHTML=" .. encode.json(html or ' ') .. ";") - end + local json_request_slots = request.get_json_request_slots() + if json_request_slots then + cgi.set_content_type('application/json') + local data = {} + for idx, slot_ident in ipairs(json_request_slots) do + data[slot_ident] = slot.get_content(slot_ident) end - -- oder ganz herkoemmlich + cgi.send_data(encode.json(data)) else cgi.set_content_type(slot.get_content_type()) cgi.send_data(slot.render_layout()) diff -r 9fdfb27f8e67 -r 985024b16520 framework/env/encode/json.lua --- a/framework/env/encode/json.lua Sun Oct 25 12:00:00 2009 +0100 +++ b/framework/env/encode/json.lua Tue Nov 17 12:00:00 2009 +0100 @@ -5,9 +5,12 @@ ) This function encodes any native datatype or atom in JavaScript object notation (JSON). +TODO: can't distinguish unambiguously between empty object and empty list! --]]-- +-- TODO: check if numeric representations are JSON compatible + function encode.json(obj) if obj == nil then return "null"; @@ -15,20 +18,49 @@ return tostring(obj) elseif atom.has_type(obj, atom.number) then return tostring(obj) + elseif type(obj) == "table" then + local parts = {} + local first = true + if #obj > 0 then + parts[#parts+1] = "[" + for idx, value in ipairs(obj) do + if first then + first = false + else + parts[#parts+1] = "," + end + parts[#parts+1] = tostring(value) + end + parts[#parts+1] = "]" + else + parts[#parts+1] = "{" + for key, value in pairs(obj) do + if first then + first = false + else + parts[#parts+1] = "," + end + parts[#parts+1] = encode.json(key) + parts[#parts+1] = ":" + parts[#parts+1] = encode.json(value) + end + parts[#parts+1] = "}" + end + return table.concat(parts) else return - "'" .. + '"' .. string.gsub(atom.dump(obj), ".", function (char) - if char == "\r" then return "\\r" end - if char == "\n" then return "\\n" end - if char == "\\" then return "\\\\" end - if char == "'" then return "\\'" end - if char == "/" then return "\\/" end -- allowed according to RFC4627, needed for + if char == '\r' then return '\\r' end + if char == '\n' then return '\\n' end + if char == '\\' then return '\\\\' end + if char == '"' then return '\\"' end + if char == '/' then return '\\/' end -- allowed according to RFC4627, needed for local byte = string.byte(char) if byte < 32 then return string.format("\\u%04x", byte) end end ) .. - "'" + '"' end end diff -r 9fdfb27f8e67 -r 985024b16520 framework/env/request/__init.lua --- a/framework/env/request/__init.lua Sun Oct 25 12:00:00 2009 +0100 +++ b/framework/env/request/__init.lua Tue Nov 17 12:00:00 2009 +0100 @@ -7,6 +7,7 @@ request._force_absolute_baseurl = false request._perm_params = {} request._csrf_secret = nil +request._json_requests_allowed = false local depth if cgi then -- if-clause to support interactive mode diff -r 9fdfb27f8e67 -r 985024b16520 framework/env/request/get_json_request_slots.lua --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/framework/env/request/get_json_request_slots.lua Tue Nov 17 12:00:00 2009 +0100 @@ -0,0 +1,15 @@ +--[[-- +slot_idents = -- list of names of slots to be returned as JSON data +request.get_json_request_slots() + +If the current request is no JSON request, this function returns nil, otherwise a list of names of all slots to be returned in JSON format. This function also throws an error, if JSON data was requested, but request.set_allowed_json_request_slots(...) has not been called. + +--]]-- + +function request.get_json_request_slots(slot_idents) + local slot_idents = cgi.params["_webmcp_json_slots[]"] + if slot_idents and not request._json_requests_allowed then + error("JSON requests have not been allowed using request.set_allowed_json_request_slots(...).") + end + return slot_idents +end diff -r 9fdfb27f8e67 -r 985024b16520 framework/env/request/is_ajax.lua --- a/framework/env/request/is_ajax.lua Sun Oct 25 12:00:00 2009 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3 +0,0 @@ -function request.is_ajax() - return cgi.params['ajax'] == '1' -end \ No newline at end of file diff -r 9fdfb27f8e67 -r 985024b16520 framework/env/request/set_allowed_json_request_slots.lua --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/framework/env/request/set_allowed_json_request_slots.lua Tue Nov 17 12:00:00 2009 +0100 @@ -0,0 +1,23 @@ +--[[-- +request.set_allowed_json_request_slots( + slot_idents -- list of names of slots which can be requested in JSON format +) + +This function enables JSON requests. The given list of names of slots selects, which slots may be requestd in JSON format (without layout). + +--]]-- + +function request.set_allowed_json_request_slots(slot_idents) + local hash = {} + for idx, slot_ident in ipairs(slot_idents) do + hash[slot_ident] = true + end + if cgi.params["_webmcp_json_slots[]"] then + for idx, slot_ident in ipairs(cgi.params["_webmcp_json_slots[]"]) do + if not hash[slot_ident] then + error('Requesting slot "' .. slot_ident .. '" is forbidden.') + end + end + end + request._json_requests_allowed = true +end diff -r 9fdfb27f8e67 -r 985024b16520 framework/env/ui/paginate.lua --- a/framework/env/ui/paginate.lua Sun Oct 25 12:00:00 2009 +0100 +++ b/framework/env/ui/paginate.lua Tue Nov 17 12:00:00 2009 +0100 @@ -3,12 +3,14 @@ selector = selector, -- a selector for items from the database per_page = per_page, -- items per page, defaults to 10 name = name, -- name of the CGI get variable, defaults to "page" + page = page, -- directly specify a page, and ignore 'name' parameter content = function() ... -- code block which should be encapsulated with page selection links end } -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. +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. The currently displayed page will be determined directly by cgi.params, and not via the param.get(...) function, in order to pass page selections automatically to sub-views. + --]]-- function ui.paginate(args) @@ -22,7 +24,7 @@ count_selector:single_object_mode() local count = count_selector:exec().count local page_count = math.floor((count - 1) / per_page) + 1 - local current_page = param.get(name, atom.integer) or 1 + local current_page = atom.integer:load(cgi.params[name]) or 1 selector:limit(per_page) selector:offset((current_page - 1) * per_page) local id = param.get_id_cgi() diff -r 9fdfb27f8e67 -r 985024b16520 libraries/atom/atom.lua --- a/libraries/atom/atom.lua Sun Oct 25 12:00:00 2009 +0100 +++ b/libraries/atom/atom.lua Tue Nov 17 12:00:00 2009 +0100 @@ -212,10 +212,10 @@ --]]-- function boolean:load(str) - if type(str) ~= "string" then + if str == nil or str == "" then + return nil + elseif type(str) ~= "string" then error("String expected") - elseif str == "" then - return nil elseif string.find(str, "^[TtYy1]") then return true elseif string.find(str, "^[FfNn0]") then @@ -244,7 +244,9 @@ --]]-- function _M.string:load(str) - if type(str) ~= "string" then + if str == nil then + return nil + elseif type(str) ~= "string" then error("String expected") else return str @@ -270,10 +272,10 @@ --]]-- function integer:load(str) - if type(str) ~= "string" then + if str == nil or str == "" then + return nil + elseif type(str) ~= "string" then error("String expected") - elseif str == "" then - return nil else local num = tonumber(str) if is_integer(num) then return num else return not_a_number end @@ -308,10 +310,10 @@ --]]-- function number:load(str) - if type(str) ~= "string" then + if str == nil or str == "" then + return nil + elseif type(str) ~= "string" then error("String expected") - elseif str == "" then - return nil else return tonumber(str) or not_a_number end @@ -463,8 +465,10 @@ --]]-- function fraction:load(str) - if str == "" then + if str == nil or str == "" then return nil + elseif type(str) ~= "string" then + error("String expected") else local sign, int = string.match(str, "^(%-?)([0-9]+)$") if sign == "" then return fraction:new(tonumber(int)) @@ -877,8 +881,10 @@ --]]-- function date:load(str) - if str == "" then + if str == nil or str == "" then return nil + elseif type(str) ~= "string" then + error("String expected") else local year, month, day = string.match( str, "^([0-9][0-9][0-9][0-9])-([0-9][0-9])-([0-9][0-9])$" @@ -1197,8 +1203,10 @@ --]]-- function timestamp:load(str) - if str == "" then + if str == nil or str == "" then return nil + elseif type(str) ~= "string" then + error("String expected") else local year, month, day, hour, minute, second = string.match( str, @@ -1418,8 +1426,10 @@ --]]-- function time:load(str) - if str == "" then + if str == nil or str == "" then return nil + elseif type(str) ~= "string" then + error("String expected") else local hour, minute, second = string.match( str, diff -r 9fdfb27f8e67 -r 985024b16520 libraries/mondelefant/mondelefant_atom_connector.lua --- a/libraries/mondelefant/mondelefant_atom_connector.lua Sun Oct 25 12:00:00 2009 +0100 +++ b/libraries/mondelefant/mondelefant_atom_connector.lua Tue Nov 17 12:00:00 2009 +0100 @@ -100,12 +100,15 @@ output_converters.date = function(str) return atom.date:load(str) end local timestamp_loader_func = function(str) - local hour, minute, second = string.match( + local year, month, day, hour, minute, second = string.match( str, - "^([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]) ([0-9]?[0-9]):([0-9][0-9]):([0-9][0-9])" ) - if hour then + if year then return atom.timestamp{ + year = tonumber(year), + month = tonumber(month), + day = tonumber(day), hour = tonumber(hour), minute = tonumber(minute), second = tonumber(second) @@ -118,15 +121,12 @@ output_converters.timestamptz = timestamp_loader_func local time_loader_func = function(str) - local year, month, day, hour, minute, second = string.match( + local hour, minute, second = string.match( str, - "^([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])" + "^([0-9]?[0-9]):([0-9][0-9]):([0-9][0-9])" ) - if year then + if hour then return atom.time{ - year = tonumber(year), - month = tonumber(month), - day = tonumber(day), hour = tonumber(hour), minute = tonumber(minute), second = tonumber(second)