Ancestry Sync Display Individual.fh_lua

--[[
@Title:       Ancestry Sync - Display Individual
@Type:        Standard
@Author:      Mark Draper
@Contributor  John Elvin
@Version:     1.0
@LastUpdated: 29 Mar 2026
@Licence:     This plugin is copyright (c) 2026 Mark Draper and is licensed under the MIT License which
is hereby incorporated by reference (see https://pluginstore.family-historian.co.uk/fh-plugin-licence)
@Description: Displays Property Box Individual in Ancestry directly.
]]

fhInitialise(7, 0, 15)
require('luacom')
require('fhSQL')
require('iuplua')

function main()

	-- get linked RM file from Ancestry Sync plugin options

	local OptionsFile = fhGetContextInfo('CI_PROJECT_DATA_FOLDER') ..
			'\\Plugin Data\\Ancestry Synchronization.ini'
	local RMfile = fhGetIniFileValue(OptionsFile, 'Files', 'File', 'text')
	if RMfile == '' then
		local msg = 'Please link the current project to Ancestry via the Ancestry Synchronization plugin.'
		MessageBox(msg, 'OK', 'ERROR')
		return
	end

	-- get currently selected Individual Record

	local tblI = fhGetCurrentRecordSel('INDI')

	if #tblI == 0 then
		local msg = 'No Individual Record selected.'
		MessageBox(msg, 'OK', 'ERROR')
		return
	elseif #tblI > 1 then
		local msg = 'Multiple Individual Records selected.'
		MessageBox(msg, 'OK', 'ERROR')
		return
	end

	local UID = fhGetItemText(tblI[1], '~._UID'):gsub('-', '')
	if UID == '' then
		local msg = 'Selected Individual does not have an assigned UniqueID. ' ..
				'Use the Ancestry Synchronization plugin to upload this individual to Ancestry.'
		MessageBox(msg, 'OK', 'ERROR')
		return
	else
		UID = FormatUID(UID)
	end

	local database = fhSQL.connectSQLite(RMfile)
	local SQL = 'SELECT anID FROM PersonTable, AncestryTable WHERE PersonID = rmID AND UniqueID = "' .. UID .. '"'
	local ResultSet = database:select(SQL)
	local anID = ''
	for I in ResultSet:rows() do
		anID = I.anID
	end

	if anID == '' then
		local msg = 'Selected Individual has not been uploaded to Ancestry.'
		MessageBox(msg, 'OK', 'ERROR')
		return
	end

	local PersonID, TreeID = anID:match('^(%d+)%:%d+%:(%d+)$')
	if PersonID and TreeID then
		local URL = 'https://www.ancestry.com/family-tree/tree/' .. TreeID .. '/person/' .. PersonID
		fhShellExecute(URL)
	else
		local msg = 'Error determining Ancestry URL.'		-- should never happen!
		MessageBox(msg, 'OK', 'ERROR')
	end
end

-- *********************************************************************

function FormatUID(UID)

	-- calculate checksum using published method

	local a = 0
	local b = 0

	for i = 1, 31, 2 do
		local byte = UID:sub(i, i + 1)
		local value = tonumber('0x' .. byte)
		a = a + value
		b = b + a
	end

	local cs1 = string.format('%x', a)
	local cs2 = string.format('%x', b)
	local checksum = cs1:sub(-2) .. cs2:sub(-2)

	return UID .. checksum:upper()
end

-- *********************************************************************

function MessageBox(Message, Buttons, Icon, Title, Default)

	-- replaces built-in function with custom version containing more options

	-- set message

	local msgdlg = iup.messagedlg{value = Message, buttons = Buttons, dialogtype = Icon,
			title = Title or 'Ancestry Synchronization - View Individual', buttondefault = Default}

	-- display message box and return selection

	msgdlg:popup()
	return tonumber(msgdlg.ButtonResponse)
end

-- *********************************************************************

main()

Source:Ancestry-Sync-Display-Individual.fh_lua