webmcp

changeset 1:985024b16520 v1.0.1

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
author jbe
date Tue Nov 17 12:00:00 2009 +0100 (2009-11-17)
parents 9fdfb27f8e67
children 72860d232f32
files LICENSE doc/autodoc-header.htmlpart framework/cgi-bin/webmcp.lua framework/env/encode/json.lua framework/env/request/__init.lua framework/env/request/get_json_request_slots.lua framework/env/request/is_ajax.lua framework/env/request/set_allowed_json_request_slots.lua framework/env/ui/paginate.lua libraries/atom/atom.lua libraries/mondelefant/mondelefant_atom_connector.lua
line diff
     1.1 --- a/LICENSE	Sun Oct 25 12:00:00 2009 +0100
     1.2 +++ b/LICENSE	Tue Nov 17 12:00:00 2009 +0100
     1.3 @@ -1,4 +1,4 @@
     1.4 -Copyright (c) 2009 Public Software Group e. V., Berlin
     1.5 +Copyright (c) 2009 Public Software Group e. V., Berlin, Germany
     1.6  
     1.7  Permission is hereby granted, free of charge, to any person obtaining a
     1.8  copy of this software and associated documentation files (the "Software"),
     2.1 --- a/doc/autodoc-header.htmlpart	Sun Oct 25 12:00:00 2009 +0100
     2.2 +++ b/doc/autodoc-header.htmlpart	Tue Nov 17 12:00:00 2009 +0100
     2.3 @@ -55,10 +55,10 @@
     2.4          color: #505050;
     2.5        }
     2.6      </style>
     2.7 -    <title>WebMCP 1.0.0 Documentation</title>
     2.8 +    <title>WebMCP 1.0.1 Documentation</title>
     2.9    </head>
    2.10    <body>
    2.11 -    <h1>WebMCP 1.0.0 Documentation</h1>
    2.12 +    <h1>WebMCP 1.0.1 Documentation</h1>
    2.13      <p>
    2.14        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.
    2.15      </p>
     3.1 --- a/framework/cgi-bin/webmcp.lua	Sun Oct 25 12:00:00 2009 +0100
     3.2 +++ b/framework/cgi-bin/webmcp.lua	Tue Nov 17 12:00:00 2009 +0100
     3.3 @@ -1,5 +1,7 @@
     3.4  #!/usr/bin/env lua
     3.5  
     3.6 +_WEBMCP_VERSION = "1.0.1"
     3.7 +
     3.8  -- include "../lib/" in search path for libraries
     3.9  do
    3.10    package.path = '../lib/?.lua;' .. package.path
    3.11 @@ -430,16 +432,14 @@
    3.12      cgi.set_status(http_status)
    3.13    end
    3.14  
    3.15 -  -- ajax
    3.16 -  if request.is_ajax() then
    3.17 -    cgi.set_content_type('text/html')
    3.18 -    for i, slot_ident in ipairs{'main', 'actions', 'title', 'topnav', 'sidenav', 'debug', 'notice', 'warning', 'error'} do
    3.19 -      local html = slot.get_content(slot_ident)
    3.20 -      if html then
    3.21 -        cgi.send_data("document.getElementById('" .. slot_ident .. "').innerHTML=" .. encode.json(html or '&nbsp;') .. ";")
    3.22 -      end
    3.23 +  local json_request_slots = request.get_json_request_slots()
    3.24 +  if json_request_slots then
    3.25 +    cgi.set_content_type('application/json')
    3.26 +    local data = {}
    3.27 +    for idx, slot_ident in ipairs(json_request_slots) do
    3.28 +      data[slot_ident] = slot.get_content(slot_ident)
    3.29      end
    3.30 -  -- oder ganz herkoemmlich
    3.31 +    cgi.send_data(encode.json(data))
    3.32    else
    3.33      cgi.set_content_type(slot.get_content_type())
    3.34      cgi.send_data(slot.render_layout())
     4.1 --- a/framework/env/encode/json.lua	Sun Oct 25 12:00:00 2009 +0100
     4.2 +++ b/framework/env/encode/json.lua	Tue Nov 17 12:00:00 2009 +0100
     4.3 @@ -5,9 +5,12 @@
     4.4  )
     4.5  
     4.6  This function encodes any native datatype or atom in JavaScript object notation (JSON).
     4.7 +TODO: can't distinguish unambiguously between empty object and empty list!
     4.8  
     4.9  --]]--
    4.10  
    4.11 +-- TODO: check if numeric representations are JSON compatible
    4.12 +
    4.13  function encode.json(obj)
    4.14    if obj == nil then
    4.15      return "null";
    4.16 @@ -15,20 +18,49 @@
    4.17      return tostring(obj)
    4.18    elseif atom.has_type(obj, atom.number) then
    4.19      return tostring(obj)
    4.20 +  elseif type(obj) == "table" then
    4.21 +    local parts = {}
    4.22 +    local first = true
    4.23 +    if #obj > 0 then
    4.24 +      parts[#parts+1] = "["
    4.25 +      for idx, value in ipairs(obj) do
    4.26 +        if first then
    4.27 +          first = false
    4.28 +        else
    4.29 +          parts[#parts+1] = ","
    4.30 +        end
    4.31 +        parts[#parts+1] = tostring(value)
    4.32 +      end
    4.33 +      parts[#parts+1] = "]"
    4.34 +    else
    4.35 +      parts[#parts+1] = "{"
    4.36 +      for key, value in pairs(obj) do
    4.37 +        if first then
    4.38 +          first = false
    4.39 +        else
    4.40 +          parts[#parts+1] = ","
    4.41 +        end
    4.42 +        parts[#parts+1] = encode.json(key)
    4.43 +        parts[#parts+1] = ":"
    4.44 +        parts[#parts+1] = encode.json(value)
    4.45 +      end
    4.46 +      parts[#parts+1] = "}"
    4.47 +    end
    4.48 +    return table.concat(parts)
    4.49    else
    4.50      return
    4.51 -      "'" ..
    4.52 +      '"' ..
    4.53        string.gsub(atom.dump(obj), ".",
    4.54          function (char)
    4.55 -          if char == "\r" then return "\\r"  end
    4.56 -          if char == "\n" then return "\\n"  end
    4.57 -          if char == "\\" then return "\\\\" end
    4.58 -          if char == "'"  then return "\\'"  end
    4.59 -          if char == "/"  then return "\\/"  end  -- allowed according to RFC4627, needed for </script>
    4.60 +          if char == '\r' then return '\\r'  end
    4.61 +          if char == '\n' then return '\\n'  end
    4.62 +          if char == '\\' then return '\\\\' end
    4.63 +          if char == '"'  then return '\\"'  end
    4.64 +          if char == '/'  then return '\\/'  end  -- allowed according to RFC4627, needed for </script>
    4.65            local byte = string.byte(char)
    4.66            if byte < 32 then return string.format("\\u%04x", byte) end
    4.67          end
    4.68        ) ..
    4.69 -      "'"
    4.70 +      '"'
    4.71    end
    4.72  end
     5.1 --- a/framework/env/request/__init.lua	Sun Oct 25 12:00:00 2009 +0100
     5.2 +++ b/framework/env/request/__init.lua	Tue Nov 17 12:00:00 2009 +0100
     5.3 @@ -7,6 +7,7 @@
     5.4  request._force_absolute_baseurl = false
     5.5  request._perm_params = {}
     5.6  request._csrf_secret = nil
     5.7 +request._json_requests_allowed = false
     5.8  
     5.9  local depth
    5.10  if cgi then  -- if-clause to support interactive mode
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/framework/env/request/get_json_request_slots.lua	Tue Nov 17 12:00:00 2009 +0100
     6.3 @@ -0,0 +1,15 @@
     6.4 +--[[--
     6.5 +slot_idents =                    -- list of names of slots to be returned as JSON data
     6.6 +request.get_json_request_slots()
     6.7 +
     6.8 +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.
     6.9 +
    6.10 +--]]--
    6.11 +
    6.12 +function request.get_json_request_slots(slot_idents)
    6.13 +  local slot_idents = cgi.params["_webmcp_json_slots[]"]
    6.14 +  if slot_idents and not request._json_requests_allowed then
    6.15 +    error("JSON requests have not been allowed using request.set_allowed_json_request_slots(...).")
    6.16 +  end
    6.17 +  return slot_idents
    6.18 +end
     7.1 --- a/framework/env/request/is_ajax.lua	Sun Oct 25 12:00:00 2009 +0100
     7.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.3 @@ -1,3 +0,0 @@
     7.4 -function request.is_ajax()
     7.5 -  return cgi.params['ajax'] == '1'
     7.6 -end
     7.7 \ No newline at end of file
     8.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.2 +++ b/framework/env/request/set_allowed_json_request_slots.lua	Tue Nov 17 12:00:00 2009 +0100
     8.3 @@ -0,0 +1,23 @@
     8.4 +--[[--
     8.5 +request.set_allowed_json_request_slots(
     8.6 +  slot_idents                            -- list of names of slots which can be requested in JSON format
     8.7 +)
     8.8 +
     8.9 +This function enables JSON requests. The given list of names of slots selects, which slots may be requestd in JSON format (without layout).
    8.10 +
    8.11 +--]]--
    8.12 +
    8.13 +function request.set_allowed_json_request_slots(slot_idents)
    8.14 +  local hash = {}
    8.15 +  for idx, slot_ident in ipairs(slot_idents) do
    8.16 +    hash[slot_ident] = true
    8.17 +  end
    8.18 +  if cgi.params["_webmcp_json_slots[]"] then
    8.19 +    for idx, slot_ident in ipairs(cgi.params["_webmcp_json_slots[]"]) do
    8.20 +      if not hash[slot_ident] then
    8.21 +        error('Requesting slot "' .. slot_ident .. '" is forbidden.')
    8.22 +      end
    8.23 +    end
    8.24 +  end
    8.25 +  request._json_requests_allowed = true
    8.26 +end
     9.1 --- a/framework/env/ui/paginate.lua	Sun Oct 25 12:00:00 2009 +0100
     9.2 +++ b/framework/env/ui/paginate.lua	Tue Nov 17 12:00:00 2009 +0100
     9.3 @@ -3,12 +3,14 @@
     9.4    selector = selector,  -- a selector for items from the database
     9.5    per_page = per_page,  -- items per page, defaults to 10
     9.6    name     = name,      -- name of the CGI get variable, defaults to "page"
     9.7 +  page     = page,      -- directly specify a page, and ignore 'name' parameter
     9.8    content = function()
     9.9      ...                 -- code block which should be encapsulated with page selection links
    9.10    end
    9.11  }
    9.12  
    9.13 -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.
    9.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. 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.
    9.15 +
    9.16  --]]--
    9.17  
    9.18  function ui.paginate(args)
    9.19 @@ -22,7 +24,7 @@
    9.20    count_selector:single_object_mode()
    9.21    local count = count_selector:exec().count
    9.22    local page_count = math.floor((count - 1) / per_page) + 1
    9.23 -  local current_page = param.get(name, atom.integer) or 1
    9.24 +  local current_page = atom.integer:load(cgi.params[name]) or 1
    9.25    selector:limit(per_page)
    9.26    selector:offset((current_page - 1) * per_page)
    9.27    local id     = param.get_id_cgi()
    10.1 --- a/libraries/atom/atom.lua	Sun Oct 25 12:00:00 2009 +0100
    10.2 +++ b/libraries/atom/atom.lua	Tue Nov 17 12:00:00 2009 +0100
    10.3 @@ -212,10 +212,10 @@
    10.4  
    10.5  --]]--
    10.6  function boolean:load(str)
    10.7 -  if type(str) ~= "string" then
    10.8 +  if str == nil or str == "" then
    10.9 +    return nil
   10.10 +  elseif type(str) ~= "string" then
   10.11      error("String expected")
   10.12 -  elseif str == "" then
   10.13 -    return nil
   10.14    elseif string.find(str, "^[TtYy1]") then
   10.15      return true
   10.16    elseif string.find(str, "^[FfNn0]") then
   10.17 @@ -244,7 +244,9 @@
   10.18  
   10.19  --]]--
   10.20  function _M.string:load(str)
   10.21 -  if type(str) ~= "string" then
   10.22 +  if str == nil then
   10.23 +    return nil
   10.24 +  elseif type(str) ~= "string" then
   10.25      error("String expected")
   10.26    else
   10.27      return str
   10.28 @@ -270,10 +272,10 @@
   10.29  
   10.30  --]]--
   10.31  function integer:load(str)
   10.32 -  if type(str) ~= "string" then
   10.33 +  if str == nil or str == "" then
   10.34 +    return nil
   10.35 +  elseif type(str) ~= "string" then
   10.36      error("String expected")
   10.37 -  elseif str == "" then
   10.38 -    return nil
   10.39    else
   10.40      local num = tonumber(str)
   10.41      if is_integer(num) then return num else return not_a_number end
   10.42 @@ -308,10 +310,10 @@
   10.43  
   10.44  --]]--
   10.45  function number:load(str)
   10.46 -  if type(str) ~= "string" then
   10.47 +  if str == nil or str == "" then
   10.48 +    return nil
   10.49 +  elseif type(str) ~= "string" then
   10.50      error("String expected")
   10.51 -  elseif str == "" then
   10.52 -    return nil
   10.53    else
   10.54      return tonumber(str) or not_a_number
   10.55    end
   10.56 @@ -463,8 +465,10 @@
   10.57  
   10.58  --]]--
   10.59  function fraction:load(str)
   10.60 -  if str == "" then
   10.61 +  if str == nil or str == "" then
   10.62      return nil
   10.63 +  elseif type(str) ~= "string" then
   10.64 +    error("String expected")
   10.65    else
   10.66      local sign, int = string.match(str, "^(%-?)([0-9]+)$")
   10.67      if sign == "" then return fraction:new(tonumber(int))
   10.68 @@ -877,8 +881,10 @@
   10.69  
   10.70  --]]--
   10.71  function date:load(str)
   10.72 -  if str == "" then
   10.73 +  if str == nil or str == "" then
   10.74      return nil
   10.75 +  elseif type(str) ~= "string" then
   10.76 +    error("String expected")
   10.77    else
   10.78      local year, month, day = string.match(
   10.79        str, "^([0-9][0-9][0-9][0-9])-([0-9][0-9])-([0-9][0-9])$"
   10.80 @@ -1197,8 +1203,10 @@
   10.81  
   10.82  --]]--
   10.83  function timestamp:load(str)
   10.84 -  if str == "" then
   10.85 +  if str == nil or str == "" then
   10.86      return nil
   10.87 +  elseif type(str) ~= "string" then
   10.88 +    error("String expected")
   10.89    else
   10.90      local year, month, day, hour, minute, second = string.match(
   10.91        str,
   10.92 @@ -1418,8 +1426,10 @@
   10.93  
   10.94  --]]--
   10.95  function time:load(str)
   10.96 -  if str == "" then
   10.97 +  if str == nil or str == "" then
   10.98      return nil
   10.99 +  elseif type(str) ~= "string" then
  10.100 +    error("String expected")
  10.101    else
  10.102      local hour, minute, second = string.match(
  10.103        str,
    11.1 --- a/libraries/mondelefant/mondelefant_atom_connector.lua	Sun Oct 25 12:00:00 2009 +0100
    11.2 +++ b/libraries/mondelefant/mondelefant_atom_connector.lua	Tue Nov 17 12:00:00 2009 +0100
    11.3 @@ -100,12 +100,15 @@
    11.4  output_converters.date = function(str) return atom.date:load(str) end
    11.5  
    11.6  local timestamp_loader_func = function(str)
    11.7 -  local hour, minute, second = string.match(
    11.8 +  local year, month, day, hour, minute, second = string.match(
    11.9      str,
   11.10 -    "^([0-9]?[0-9]):([0-9][0-9]):([0-9][0-9])"
   11.11 +    "^([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])"
   11.12    )
   11.13 -  if hour then
   11.14 +  if year then
   11.15      return atom.timestamp{
   11.16 +      year   = tonumber(year),
   11.17 +      month  = tonumber(month),
   11.18 +      day    = tonumber(day),
   11.19        hour   = tonumber(hour),
   11.20        minute = tonumber(minute),
   11.21        second = tonumber(second)
   11.22 @@ -118,15 +121,12 @@
   11.23  output_converters.timestamptz = timestamp_loader_func
   11.24  
   11.25  local time_loader_func = function(str)
   11.26 -  local year, month, day, hour, minute, second = string.match(
   11.27 +  local hour, minute, second = string.match(
   11.28      str,
   11.29 -    "^([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])"
   11.30 +    "^([0-9]?[0-9]):([0-9][0-9]):([0-9][0-9])"
   11.31    )
   11.32 -  if year then
   11.33 +  if hour then
   11.34      return atom.time{
   11.35 -      year   = tonumber(year),
   11.36 -      month  = tonumber(month),
   11.37 -      day    = tonumber(day),
   11.38        hour   = tonumber(hour),
   11.39        minute = tonumber(minute),
   11.40        second = tonumber(second)

Impressum / About Us