Module Require With Load

In addition to the standard modules installed with Family Historian, Lua has a range of additional modules available. In ƒh7 many of these are installed with the programme, but in ƒh6, they may need to be downloaded before use. See Lua References and Library Modules for details of which libraries are available and how.

If you require additional modules for ƒh6, please contact  [email protected] requesting the module to be added to the repository.

Requires: lfs and luacom

Updated code (multiple sub-directories)

require 'lfs'
require 'luacom'

local function httpRequest(url)
  local http = luacom.CreateObject("winhttp.winhttprequest.5.1")
  http:Open("GET",url,false)
  http:Send()
  http:WaitForResponse(30)
  return http
end -- local function httpRequest


function loadrequire(module,extended)
  if not(extended) then extended = module end
  local function installmodule(module,filename,size)
    local function makesubdirs(path)
      local sep, pStr = package.config:sub(1, 1), ""
      for dir in path:gmatch("[^" .. sep .. "]+") do
        pStr = pStr .. dir .. sep
        lfs.mkdir(pStr)
      end        
    end

    local bmodule = false
    if not(filename) then
      filename = module..'.mod'
      bmodule = true
    end
    local storein = fhGetContextInfo('CI_APP_DATA_FOLDER')..'\\Plugins\\'
    -- Check if subdirectory needed
    local path = string.match(filename, "(.-)[^/]-[^%.]+$")
    if path ~= "" then
      path = path:gsub('/','\\')
      -- Create sub-directory
      makesubdirs(storein..path)
    end
    local attr = lfs.attributes(storein..filename)
    if attr and attr.mode == 'file' and attr.size == size then return true end
    -- Get file down and install it
    local url = "http://www.family-historian.co.uk/lnk/getpluginmodule.php?file="..filename
    local isOK, reply = pcall(httpRequest,url)
    if not isOK then
      fhMessageBox(reply.."\nLoad Require module finds the Internet inaccessible.")
      return false
    end
    local http = reply
    local status = http.StatusText
    if status == 'OK' then
      length = http:GetResponseHeader('Content-Length')
      data = http.ResponseBody
      if bmodule then
        local modlist = loadstring(http.ResponseBody)
        for x,y in pairs(modlist()) do
          if type(x) == 'number' and type(y) == 'string' then
            x = y -- revert to original 'filename' ipairs modlist
            y = 0
          end -- otherwise use ['filename']=size pairs modlist
          if not(installmodule(module,x,y)) then
            break
          end
        end
      else
        local function OpenFile(strFileName,strMode)
          local fileHandle, strError = io.open(strFileName,strMode)
          if not fileHandle then
            error("\n Unable to open file in \""..strMode.."\" mode. \n "..
              strFileName.." \n "..tostring(strError).." \n")
          end
          return fileHandle
        end -- OpenFile
        local function SaveStringToFile(strString,strFileName)
          local fileHandle = OpenFile(strFileName,"wb")
          fileHandle:write(strString)
          assert(fileHandle:close())
        end -- SaveStringToFile
        SaveStringToFile(data,storein..filename)
      end
      return true
    else
      fhMessageBox('An error occurred in Download, so please try later')
      return false
    end
  end
  local function requiref(module)
    require(module)
  end
  local res, err = pcall(requiref,extended)
  if err then
    if  err:match("module '"..extended:gsub("(%W)","%%%1").."' not found") then
      local ans = fhMessageBox(
        'This plugin requires '..module..' support, please click OK to download and install the module, which may take a few minutes in some systems',
        'MB_OKCANCEL','MB_ICONEXCLAMATION')
      if ans ~= 'OK' then
        return false
      end
      if installmodule(module) then
        package.loaded[extended] = nil -- Reset Failed Load
        require(extended)
      else
        return false
      end
    else
      fhMessageBox('Error from require("'..module..'") command:\n'..(err or ''))
      return false
    end
  end
  return true
end

 

Code (single sub-directory)

require 'lfs'
require 'luacom'
 
local function httpRequest(url)
    local http = luacom.CreateObject("winhttp.winhttprequest.5.1")
    http:Open("GET",url,false)
    http:Send()
    http:WaitForResponse(30)
    return http
end -- local function httpRequest
 
 
function loadrequire(module,extended)
    if not(extended) then extended = module end
    local function installmodule(module,filename,size)
        local bmodule = false
        if not(filename) then
            filename = module..'.mod'
            bmodule = true
        end
        local storein = fhGetContextInfo('CI_APP_DATA_FOLDER')..'\\Plugins\\'
        -- Check if subdirectory needed
        local path = string.match(filename, "(.-)[^/]-[^%.]+$")
        if path ~= "" then
            path = path:gsub('/','\\')
            -- Create sub-directory
            lfs.mkdir(storein..path)
        end
        local attr = lfs.attributes(storein..filename)
        if attr and attr.mode == 'file' and attr.size == size then return true end
        -- Get file down and install it
        local url = "http://www.family-historian.co.uk/lnk/getpluginmodule.php?file="..filename
        local isOK, reply = pcall(httpRequest,url)
        if not isOK then
            fhMessageBox(reply.."\nLoad Require module finds the Internet inaccessible.")
            return false
        end
        local http = reply
        local status = http.StatusText
        if status == 'OK' then
            length = http:GetResponseHeader('Content-Length')
            data = http.ResponseBody
            if bmodule then
                local modlist = loadstring(http.ResponseBody)
                for x,y in pairs(modlist()) do
                    if type(x) == 'number' and type(y) == 'string' then
                        x = y -- revert to original 'filename' ipairs modlist
                        y = 0
                    end -- otherwise use ['filename']=size pairs modlist
                    if not(installmodule(module,x,y)) then
                        break
                    end
                end
            else
                local function OpenFile(strFileName,strMode)
                    local fileHandle, strError = io.open(strFileName,strMode)
                    if not fileHandle then
                        error("\n Unable to open file in \""..strMode.."\" mode. \n "..
                        strFileName.." \n "..tostring(strError).." \n")
                    end
                    return fileHandle
                end -- OpenFile
                local function SaveStringToFile(strString,strFileName)
                    local fileHandle = OpenFile(strFileName,"wb")
                    fileHandle:write(strString)
                    assert(fileHandle:close())
                end -- SaveStringToFile
                SaveStringToFile(data,storein..filename)
            end
            return true
        else
            fhMessageBox('An error occurred in Download, so please try later')
            return false
        end
    end
    local function requiref(module)
        require(module)
    end
    local res, err = pcall(requiref,extended)
    if err then
     if  err:match("module '"..extended:gsub("(%W)","%%%1").."' not found") then
        local ans = fhMessageBox(
        'This plugin requires '..module..' support, please click OK to download and install the module, which may take a few minutes in some systems',
        'MB_OKCANCEL','MB_ICONEXCLAMATION')
        if ans ~= 'OK' then
            return false
        end
        if installmodule(module) then
            package.loaded[extended] = nil -- Reset Failed Load
            require(extended)
        else
            return false
        end
     else
        fhMessageBox('Error from require("'..module..'") command:\n'..(err or ''))
        return false
    end
  end
  return true
  end

Usage

Instead of a simple require statement, use the following and the module will be checked for and installed if required.

--  Standard Modules
if not( loadrequire( 'md5' ) ) then return end
if not( loadrequire( 'zip' ) ) then return end

-- LuaSql and other modules with extensions need the module to load to be specified e.g.
if not( loadrequire( 'luasql', 'luasql.sqlite3' ) ) then return end
if not( loadrequire( 'pl', 'pl.init' ) ) then return end
pl = require 'pl.import_into'()