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, please contact  support@family-historian.co.uk requesting the module to be added to the repository.

Requires: lfs and luacom

Code

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, or perform a manual download from the FHUG Knowledge Base')
            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'()

Last update: 07 Jan 2021