Clone Any Record.fh_lua

--[[
@Title: Clone Any Record
@Type: Standard
@Author: Peter Richmond
@Contributors: Mike Tate
@Version: 3.0
@LastUpdated: 02Aug2021
@Licence: This plugin is copyright (c) 2021 Peter Richmond & contributors and is licensed under the MIT License which is hereby incorporated by reference (see https://pluginstore.family-historian.co.uk/fh-plugin-licence)
@Description: Creates one or more clones of a selected record.
Useful for creating new records (especially sources) based on a template.
It is best to add this plugin to the Tools menu and run it from there after selecting the
required "Template" record in the Records Window.
A message confirms creation of new record(s) and (if only one) full details are optionally shown as a result set.
You then need to change the name/title of each new record and complete any required non-template fields.
The template record can contain blank fields if wanted, and links to other records.
User is prompted to confirm that an Individual or Family record should be cloned.
Note that if an Individual record is cloned the new record will contain an empty Family-as-Spouse link
which can be either appropriately completed or deleted.
Updated 14 Jan 2021 to work with FH7 as well as FH5 & FH6.
Updated 02 Aug 2021 to v3.0 to also allow multiple (2 to 50) clones; default behaviour same as previous version.
]]

require("iuplua")

function CopyBranch(ptrSource,ptrTarget)
  local strTag = fhGetTag(ptrSource)
  if strTag == "_FMT" then return end    -- Skip rich text format code
  if strTag == "_FIELD" then             -- Substitute metafield shortcut
    strTag = fhGetMetafieldShortcut(ptrSource)
  end
  local ptrNew = fhCreateItem(strTag,ptrTarget,true)
  if ptrNew:IsNull() then return end     -- Escape if item not created
  fhSetValue_Copy(ptrNew,ptrSource)
  CopyChildren(ptrSource,ptrNew)
end -- function CopyBranch cribbed from FHUG KB (with Mike Tate correction)

function CopyChildren(ptrSource,ptrTarget)
  local ptrFrom = fhNewItemPtr()
  ptrFrom = ptrSource:Clone()
  ptrFrom:MoveToFirstChildItem(ptrFrom)
  while ptrFrom:IsNotNull() do
    CopyBranch(ptrFrom,ptrTarget)
    ptrFrom:MoveNext()
  end
end -- function CopyChildren cribbed from FHUG KB

function BuildDR(ptr)
   local ptrTemp = fhNewItemPtr()
   ptrTemp:MoveTo(ptr)
   strDR = fhGetTag(ptrTemp)
   while fhHasParentItem(ptrTemp) do
      ptrTemp:MoveToParentItem(ptrTemp)
      strDR = fhGetTag(ptrTemp)..'.'..strDR
   end
   return strDR
end

------------------------------- Main
tRec = fhGetCurrentRecordSel()		-- check that one and only one record has been selected
if #tRec ~= 1 then
    fhMessageBox('You must first select one "template" record')
else
	pTemplate = tRec[1]		-- Pointer for "Template" record
	sTag = fhGetTag(pTemplate)		-- Type of record being copied

	if sTag == 'HEAD' then
		fhMessageBox('You cannot clone the Header record','MB_OK','MB_ICONSTOP')
		return
	end
	if sTag == 'SUBN' then
		fhMessageBox('You cannot clone the Submission record','MB_OK','MB_ICONSTOP')
		return
	end
	if sTag == 'INDI' or sTag == 'FAM' then
		a = fhMessageBox('Are you SURE you want to clone '..sTag..' record "'..fhGetDisplayText(pTemplate)
		..'" (Id = '..fhGetRecordId(pTemplate)..')?',"MB_OKCancel","MB_ICONQUESTION")
		if a ~= "OK" then return end
	end

-- Dialog for user to specify required number of clones.
bOK, iNumber, iOutput = iup.GetParam("Clone Any Record", param_action,
"Please specify number of clones required: %i[1,50]\n" ..
"Show result set (available if only 1 clone)?: %b[No,Yes]\n",1,1)
if bOK then
for i=1,iNumber do
	pNewRecord = fhCreateItem(sTag)		-- Create new record of same type as template
	CopyChildren(pTemplate, pNewRecord)		-- Copy all details from template to new record
end

sUndo = "\n\nYou can use FH menu option Edit > Undo Plugin Updates if you don't like the result."
if iNumber == 1 then sMsg = "New "..fhGetTag(pNewRecord)..' record: "'..fhGetDisplayText(pNewRecord)..'" (Id = '..fhGetRecordId(pNewRecord)..')'
	else sMsg = iNumber.." clones of "..fhGetDisplayText(pNewRecord)
end
fhMessageBox(sMsg.." created."..sUndo)

if iOutput == 1 and iNumber == 1 then		-- Optionally display result set for single clone
	tItems = {}		-- Define array for Items
	tDRs = {}		-- Define array for Data References
	tValues = {}		-- Define array for Values of Items
	tClass = {}		-- Define array for DataClass of Items
	pi = fhNewItemPtr()		-- declare pointers
	pi2 = pNewRecord
	pi1 = pi2:Clone()

	pi:MoveTo(pi2)
	while pi:IsNotNull() and pi1:IsSame(pi2) do
		table.insert(tItems,pi:Clone())
		table.insert(tDRs,BuildDR(pi))
		table.insert(tValues,fhGetDisplayText(pi,'~','STD'))
		table.insert(tClass,fhGetDataClass(pi,'~'))
		pi:MoveNextSpecial()
		pi1:MoveToRecordItem(pi)
	end

	sTitle = 'Data List for new '..sMsg
	fhOutputResultSetTitles(sTitle, sTitle, "     %#c")
	fhOutputResultSetColumn('DataRef (w/o Index)', 'text', tDRs, #tDRs, 100)
	fhOutputResultSetColumn('DataClass', 'text', tClass, #tClass, 40)
	fhOutputResultSetColumn('Item', 'item', tItems, #tItems, 180)
	fhOutputResultSetColumn('ValueStd', 'text', tValues, #tValues, 240)
end
end
end

Source:Clone-Any-Record-2.fh_lua